Anticipez les besoins en consommation électrique de bâtiments

Sommaire

Préambule

Problématique

La ville de Seattle s'intéresse de près aux émissions
des bâtiments non destinés à l'habitation.

Les agents de la ville ont réalisé des relevés minutieux
dans les bâtiments de la ville en 2015 et en 2016.
Cependant, les relevés sont couteux à obtenir.

Est-il possible de prédire, et donc de se passer, des relevés des agents
pour les années à venir ?
Et donc de calculer les émissions de CO2 et la consommation totale d'énergie
des bâtiments non destinés à l'habitation, en s'appuyant uniquement
sur les relevés déjà effectués en 2015 et 2016
?

Notre Objectif dans ce projet

Tenter de prédire les émissions de CO2 et la consommation totale d'énergie
de nouveaux bâtiments en s'appuyant sur les relevés des agents de la ville
de Seattle en 2015 et 2016.

L'idée est d'explorer ces données, de les nettoyer, de les comprendre
et de les optimiser afin d'être utilisées à travers des algorithmes de Machine Learning.
Nous verrons en détail chacune de ces étapes.

Nous testerons, nous comparerons et nous optimiserons
différents Algorithmes de Machine Learning pour enfin conclure
sur la question qui nous est posée
: Peut-on prédire efficacement les émissions de CO2
et la consommation totale d'énergie pour les années à venir sans devoir réaliser de
nouveaux relevés par nos agents de terrain ?

Nous chercherons également à évaluer l'intérêt de l'**ENERGYSTARScore** pour la prédiction d'émissions.
En effet, si cette donnée peut sembler très pertinente,
elle a le désavantage d'être fastidieuse, et donc couteuse, à calculer.

En d'autres termes, nous répondrons à la question : Est-il rentable d'utiliser
l'ENERGYSTARScore pour effectuer nos prédictions en émissions ?

Import des librairies

Autres réglages

Définition du **R**andom_**S**tate pour la reproductibilité des résultats :
J'utilise dans ce projet un random_state à 1 mais je laisse le choix
à l'utilisateur de le changer facilement s'il le souhaite afin d'observer
l'influence que celà peut avoir sur les scores obtenus.

Le même Random_State sera défini dans l'ensemble
des processus du projet (où il est possible de l'utiliser)

Modification des paramètres de Pandas concernant
le nombre de lignes et colonnes pouvant être affichées
ainsi que le format d'affichage des nombres réels :

Importation des données dans des DataFrames

Pré-Traitement des DataFrames

Visualisation des 2 DataFrames

J'utilise une heatmap qui met en évidence les valeurs
manquantes en blanc
et les valeurs renseignées en bleu foncé.

J'utiliserai cette heatmap régulièrement durant mon analyse.

Traitement de la colonne 'Location' de df15

La colonne 'Location' contient des dictionnaires imbriqués.
Les clés de ces dictionnaires correspondent à des colonnes du DataFrame de 2016.
Je vais extraire ces données et renommer les colonnes afin de pouvoir merger
correctement les deux DataFrames.

A noter : La colonne 'Zip Codes' de df15 ne correspond pas à la colonne 'ZipCode' de df16.
La colonne contenant le Zip Code dans le df15 correspondant au Zip Code de df16 est
contenu dans la colonne 'Location' de df15.

La forme des données représente un dictionnaire imbriqué, encodé sous forme d'une *String* :

Extraction et Merge des colonnes issues des données encodées dans la colonne '**Location**' de *df15* à *df15* :

Suppression de la colonne '**Location**' :

Renommage des colonnes avant merge des DataFrames

Je renomme mes **Targets** d'autres colonnes pour plus de lisibilité :

Merge des 2 DataFrame

Les noms des colonnes étant maintenant parfaitement alignés,
il est possible de merger correctement les 2 DataFrames.**'

Renommage des colonnes 'SiteEnergyUse(kBtu)'
et 'PropertyGFABuilding(s)'

Pour plus de lisibilité je renomme la colonne '**SiteEnergyUse(kBtu)**' sans son unité énergétique *kBtu*
ainsi que la colonne '**PropertyGFABuilding(s)**' sans ses *parenthèses*
:

Visualisation du nouveau DataFrame '**df**' :

Features Engineering

Création de la variable 'ageBuilding'

Il me semble pertinent de remplacer les deux colonnes :

par une nouvelle colonne nommé 'ageBuilding' qui nous indique
depuis combien de temps un bâtiment à été construit.

Nous conservons autant d'informations avec moins de colonnes ce qui est apréciable,
autant d'un point de vue de la lisibilité, qu'en optimisation pour le Machine Learning.

Ajout des colonnes indiquant la nature et
la proportion des sources d'énergie utilisées

La Dataset recense 4 sources d'énergie différentes :

Je vais créer 4 nouvelles colonnes qui indiqueront, pour chaque bâtiment,
la proportion utilisée pour chaque énergie.

Au préalable :

  1. Je supprime les enregistrements ayant une valeur Null
    ou inférieur ou égale à 0 dans la colonne 'SiteEnergyUse'
  2. Je renseigne à 0 les valeur Null des colonnes :
    • Electricity(kBtu)
    • SteamUse(kBtu)
    • NaturalGas(kBtu)
    • OtherFuelUse(kBtu)
  3. Je crée la colonne 'Total_EnergyUse' qui correspond au total des 4 colonnes
    de relevé d'énergie pour vérifier si leur total correspond bien au total indiqué
    dans la colonne 'SiteEnergyUse'.

La différence entre les colonnes 'SiteEnergyUse' et 'Total_EnergyUse'
est minime, mais elle existe cependant.
Pour cette raison, je choisis de calculer les proportions de chaque colonne
à partir de l'addition des 4 colonnes existantes c'est à dire via la colonne 'Total_EnergyUse'.

Je vérifie si la valeur absolue de 1 moins le rapport
entre '**Total_EnergyUse**' et '**SiteEnergyUse**'
reste minime sur l'ensemble du DataFrame
:

Certains enregistrements ont des valeurs différentes de presque 140% !

Affichage de la valeur absolue de 1 moins le rapport entre
'**Total_EnergyUse**' et '**SiteEnergyUse**'
:

Pour plus de fiabilité, je filtre et retire les enregistrements qui ont un rapport de plus de 1%
entre la valeur absolue de 1 moins le rapport entre '**Total_EnergyUse**' et '**SiteEnergyUse**'
:

Je crée les 4 colonnes contenant les proportions
d'énergie utilisées via la fonction *calcProportionEnergies*
:

Je peux maintenant retirer la colonne '**Total_EnergyUse**' :

Affichage du DataFrame '**df**' via ses valeurs manquantes :

Analyse métier

Suppression des données Résidentielles

La colonne 'BuildingType' nous indique la nature des bâtiments
et nous permet d'identifier les bâtiments résidentiels et non résidentiels.

Je ne conserve que les bâtiments **Non Résidentiels** :

Suppression des données provenant
d'un relevé par un agent de terrain

L'objectif du projet est de tenter de prédire au mieux les données
de consommation énergétique et d'émission de CO2 sans l'aide
des relevés des agents de terrain.

Je supprime donc toutes les données
provenant d'un relevé réalisé par un agent
:

Suppression manuelle des colonnes inutiles

J'itère les modalités des colonnes et j'identifie les colonnes constantes :

J'analyse également les colonnes sur la base de leur description
contenue dans les fichiers JSON du projet.

J'affiche également les différentes valeurs des variables *Object* pour identifier
des variables qui auront besoin d'être formatées suite au merge des deux DataFrames
correspondant aux années 2015 et 2016
:

J'identifie les colonnes suivantes comme étant inutiles :

Suppression des entrées dont les targets
ont des valeurs négatives ou null

Il n'existe pas d'enregistrement pour lesquelles les targets ont une valeur négative ou égale à 0.

Pré-Traitement des DataFrames 2/2

Correction et Formatage des données suite
aux Merges des DataFrames 'df15' et 'df16'

Suite au listing des valeurs des colonnes Object réalisé précédement,
j'ai identifié des colonnes à formater (les valeurs de 2015 doivent
avoir le même format, orthographe ou casse que les valeurs de 2016).

Formatage des entrées de la colonne 'DefaultData'

La colonne **DefaultData** ne possède pas de valeur Null, je peux la convertir en colonne de type *Booleen* :

Normalisation des entrées de la colonne 'Address'

Conversion au format List des données de la colonne 'YearsENERGYSTARCertified'

Les entrées (des années) sont soit séparées par une virgule,
soient collées les unes aux autres sans séparateur.

Je formate cette colonne et transforme les données en *List* :

Quelques exemples du résultat :

Correction des encodages de la colonne 'PrimaryPropertyType'

Je supprime l'encodage "\n" présent dans certaines occurrences :

Correction de la casse et correction de notation de la colonne 'Neighborhood'

Correction de la casse de la colonne 'Outlier'

Conversion au format List des données de la colonne 'ListOfAllPropertyUseTypes'

La colonne 'ListOfAllPropertyUseTypes' contient des chaînes de caractère
contenant des éléments séparés par une virgule.

Je crée une fonction permettant de convertir cette colonne au format List
en gérant également les cas particuliers où des virgules peuvent être à l'intérieur d'éléments.
Il ne faut donc pas les considérer comme des séparateurs.

Je convertis sous forme de List les valeurs contenu dans 'ListOfAllPropertyUseTypes'

Identification des Targets

Identification et définition des Targets

Voici décrit ci-dessous les deux données, normalement calculées par les agents de terrain,
que nous allons chercher à prédire à partir des données récoltées par les agents en 2015 et 2016.

'**TotalGHGEmissions**' :

'**SiteEnergyUse**' :

Pour information :

Réarangement des colonnes Targets
dans le DataFramme 'df'

Pour plus de lisibilité, je réarrange les colonnes du DataFrame
et place les colonnes Targets sur la droite
:

Analyse de la forme des données

Maintenant que nos données sont correctement formatées,
attardons nous un peu sur les caractéristiques de notre DataFrame '**df**'
:

Dimension du DataFrame 'df'

Analyse des types de données du DataFrame

Identification des variables catégorielles

Je répartie mes colonnes en 3 catégories :

Analyse des Targets: 'TotalGHGEmissions' et 'SiteEnergyUse'

La distribution des targets est unimodale non centrée.

Traitement des valeurs manquantes

Visualisation des valeurs manquantes
du DataFrame 'df' avant traitement

Affichage des colonnes et de leur *taux de vide* correspondant :

Je choisis les stratégies suivantes pour renseigner mes valeurs manquantes :

'Outlier'

'2010 Census Tracts' et 'City Council Districts'

'YearsENERGYSTARCertified'

'LargestPropertyUseType', 'LargestPropertyUseTypeGFA',
'SecondLargestPropertyUseType', 'SecondLargestPropertyUseTypeGFA',
'ThirdLargestPropertyUseType', 'ThirdLargestPropertyUseTypeGFA'

Ci-dessous, je décris la stratégie adoptée, pour chaque enregistrement
de notre jeu de données, pour renseigner les 6 colonnes lorsque les champs sont vides.

Je ferai référence à des dictionnaires dont les définitions
et les explications seront présentées un peu plus bas.

Renseignement de la colonne '**LargestPropertyUseType**' :

Je renseigne les valeurs manquantes de la colonne 'LargestPropertyUseType'
à partir du contenu de la colonne 'PrimaryPropertyType'
et du dictionnaire dictLinkListEachPrimaryPT (que je nommerai dictKey ci-dessous
par souci de lisibilité).

Renseignement de la colonne '**SecondLargestPropertyUseType**' :

Je renseigne les valeurs manquantes de la colonne 'SecondLargestPropertyUseType'
à partir du contenu des colonnes 'NbListOfAllPropertyUseTypes', 'ListOfAllPropertyUseTypes',
'PrimaryPropertyType' et du dictionnaire dictKey

Renseignement de la colonne '**ThirdLargestPropertyUseType**' :

Renseignement des colonnes '**LargestPropertyUseTypeGFA**',
'**SecondLargestPropertyUseTypeGFA**' et '**ThirdLargestPropertyUseTypeGFA**'
:

Je renseigne les valeurs manquantes des colonnes 'LargestPropertyUseTypeGFA',
'SecondLargestPropertyUseTypeGFA' et 'ThirdLargestPropertyUseTypeGFA'
à partir du contenu des colonnes correspondantes (sans GFA) et
du dictionnaire dictOfEachPropertyUses (que je nommerai dictMedian ci-dessous par souci de lisibilité)

Je crée une colonne '**NbListOfAllPropertyUseTypes**' qui contient
le nombre d'éléments dans chaque List de '**ListOfAllPropertyUseTypes**'
:

J'ajoute immédiatement cette nouvelle colonne dans la List [Ordre_Distance] :

Je remplis un dictionnaire contenant comme clés l'ensemble des entrées existantes dans 'ListOfAllPropertyUseTypes'
et comme valeurs les médianes associées pour la colonne 'LargestPropertyUseTypeGPA'
Je renseigne 0 si aucune entrée n'existe pour une clé donnée dans la colonne 'LargestPropertyUseType'.

Affichage du contenu du *dictionnaire* '**dictOfEachPropertyUses**' :

Je réalise la même action que précedement mais pour la colonne '**PrimaryPropertyType**' :
Il est à noter que les clés ne sont pas exactement les mêmes entre les deux dictionnaires**

Les clés des deux dictionnaires étant différentes, je crée le dictionnaire 'dictLinkListEachPrimaryPT'.
Il renseigne la correspondance entre les clés respectives des dictionnaires 'dictOfEachPropertyUses' et 'dictOfEachPrimaryPropertyType'.
A chaque clé du dictionnaire 'dictLinkListEachPrimaryPT' correspond une clé du dictionnaire 'dictOfEachPrimaryPropertyType'.
A chaque valeur du dictionnaire 'dictLinkListEachPrimaryPT' correspond une ou plusieurs (List) valeurs du dictionnaire 'dictOfEachPropertyUses'.

Définition de la fonction permettant de renseigner les valeurs manquantes
des 6 colonnes selon la stratégie décrite plus haut
:

Renseignement des 6 colonnes :

'ZipCode'

La recherche de correspondance avec le champs 'OSEBuildingID' ne fonctionne malheureusement pas
sur l'ensemble des valeurs manquantes de mon jeu de données.
Lorsque la correspondance avec le champs 'OSEBuildingID' n'est pas possible,
je retourne le 'ZipCode' du bâtiment géographiquement le plus proche.
Pour déterminer le bâtiment le plus proche, je choisis le 'ZipCode' disponible du bâtiment
ayant la valeur la plus proche de la valeur absolue du produit de la 'Latitude' et de la 'Longitude'.
Cette méthode, bien qu'imprécise au niveau mondial, semble très bien fonctionner à l'échelle d'une ville,
les vérifications manuelles réalisées ont montré que les résultats obtenus étaient corrects.

J'écris la fonction me permettant d'appliquer ma stratégie
puis je renseigne ma colonne '**ZipCode**' avec cette fonction
:

La colonne '**ZipCode**' ne contient plus de valeur **Null**,
je peux maintenant la convertir au format **int**
:

'NumberofFloors'

J'effectue ici une recherche de correspondance avec la colonne 'OSEBuildingID'.
Si la recherche de correspondance ne fonctionne pas, je retourne la valeur -99.
Une fois les valeurs manquantes renseignées je nettoie cette colonne en retirant
toutes les valeurs négatives.

'ENERGYSTARScore'

Un des objectifs de ce projet est de juger de la pertinence
de devoir calculer l'ENERGYSTARScore.

Il n'est donc pas pertinent de chercher à compléter artificiellement cette colonne,
cela biaiserait les résultats et nous éloignerait de notre objectif.

Je ne complète pas cette colonne et je filtrerai le DataFrame 'df'
sur les valeurs Non Null de 'ENERGYSTARScore' lorsque je souhaiterai étudier
l'importance de cette caractéristique.

Visualisation des valeurs manquantes du DataFrame df après traitement

Vérifions plus précisément s'il reste des valeurs manquantes hors colonne 'ENERGYSTARScore**' :

Les valeurs manquantes ont bien toutes été traitées.

Affichons pour terminer cette partie,
les dimensions du Dataframe '**df**'
:

Traitement des doublons

Des bâtiments ont été analysés par les agents sur le terrain d'une année sur l'autre.
Ils constituent des doublons qu'il faut analyser.
Les bâtiments sont désignés par leurs id et sont renseignés dans la colonne 'OSEBuildingID'.

J'identifie 2 types de doublons :

3144 bâtiments ont un 'OSEBuildingID' présent sur l'année 2015 et l'année 2016.
Seulement 119 bâtiments ne sont présents que sur l'une des deux années 2015 ou 2016.

Pour décider quel doublon garder, je vais m'appuyer sur
les informations contenues dans les colonnes
:

'DefaultData' indique si des données par défaut pour
au moins une caractéristique de la propriété ont été utilisées.
Un enregistrement ayant cette caractéristique à False sera plus précis
qu'un enregistrement qui a cette caractéristique à True.

'DefaultData' est souvent à False pour l'année 2016
lorsqu'il y a doublon sur les 2 années, mais ce n'est pas systématique.

Stratégie de conservation adoptée pour chaque bâtiment analysé :

Je filtre 'df' en affichant que les 'OSEBuildingID'
qui ont leur valeur 'DefaultData' à True.
Puis j'affiche le df trié de manière ascendante
sur les colonnes 'OSEBuildingID' et 'ageBuilding' afin
de regrouper les doublons deux à deux.

Enfin, je filtre les colonnes en affichant que les 5 qui nous intéressent ici :

Pour sélectionner efficacement quel doublon conserver pour chaque doublon de mon DataFrame,
je crée une nouvelle colonne qui indiquera si la ligne est sélectionnée ou non.
Je renseignerai cette ligne grâce à la fonction selectDoublonOSEBuildingID ci-dessous :

J'indique dans une nouvelle colonne '**validationOSEBuildingID**',
les bâtiments à *conserver* ou à *supprimer*
:

Il reste 1776 bâtiments.

Pour contrôler, nous pouvons afficher les colonnes qui sont entrées dans le processus de décision
et observer le choix final dans la colonne '**validationOSEBuildingID**'
:

Je filtre mon DataFrame '**df**' avec les batiments séléctionnés :

Je supprime les deux colonnes '**OSEBuildingID**' et '**validationOSEBuildingID**' de mon DataFrame :

Traitement des Outliers

Les outliers (ou valeurs aberrantes) sont généralement définis
comme toutes valeurs extrêmes, supérieures ou inférieures à X fois
l’écart interquartile IQR.
Généralement X vaut '1,5'.
L’écart interquartile IQR correspond à la différence entre la valeur
du 3ème quartile et la valeur du 1er quartile.

Une donnée identifiée comme outlier ne permet pas,
à priori, de juger de la pertinence de la présence d'une valeur dans l'échantillon.
Une analyse métier doit être faite pour distinguer les valeurs
légitimes qu'il faut conserver des valeurs abérrantes issues d'une
erreur (humaine, de convertion d'unité, etc.) et qui doivent être supprimées.

Ici je vais commencer par visualiser les outliers de nos différentes variables quantitatives.
Ensuite je filtrerai les valeurs identifiées comme aberrantes en utilisant la distance de Cook.

Cependant, je commencerai par éliminer quelques Outliers
sur la base d'une analyse métier que je décrierai ci-dessous.

Dimension du DataFrame '**df**' avant traitement des Outliers :

Traitement manuel

D'après Wikipédia, le plus haut immeuble de Seattle à 76 étages.
Je supprime les enregistrements d'immeubles ayant plus de 76 étages :

Je supprime également les enregistrements qui ont
leur '**LargestPropertyUseType**' plus petit ou égal à 0
:

Je supprime les bâtiments qui n'ont qu'un seul type de propriété
et dont la surface totale du bâtiment est supérieure à au moins 2 fois
la superficie de cette propriété
:

Il me parrait aberrant d'avoir des enregistrements avec
des variables 'PropertyGFATotal',
qui correspondent à la superficie totale de la propriété,
être inférieures à la variable 'LargestPropertyUseTypeGFA',
qui elle, n'est qu'une composante de la surface de cette propriété.

Je supprime donc ces valeurs aberrantes :

Et enfin, je supprime toutes les valeurs négatives
des colonnes renseignant sur des surfaces
:

Je mets à jour ma liste des variables continues,
présente dans la List Ordre_Distance
:

Commençons par afficher sous forme de BoxPlot,
la distribution de nos données de nos variables continues
:

Nous attarderons ici sur les Outliers identifiés dans Seaborn et
affichés sous forme de losanges et définis selon
le calcul (+ ou -)1.5
l'écart interquartile.*

Traitement avec la distance de Cook

La distance de Cook est une mesure de l’influence d’un point de données.
C’est un moyen de trouver des valeurs aberrantes influentes dans un ensemble
de variables prédictives lors de l’exécution d’une analyse des moindres carrés.

Concrètement, nous calculons une Régression Linéaire sur notre modèle,
puis nous supprimons 1 à 1 les points de notre modèle en recalculant
à chaque fois une Régression Linéaire.
Si le score de la Régression Linéaire change drastiquement,
alors ce point est considéré comme très influent.

Ces points influents sont des valeurs extrêmes qui peuvent être des valeurs aberrantes.
Dans ce projet, je décide de filtrer toutes valeurs extrêmes identifiées par la **Distance de Cook**..

J'applique la méthode sur mes variables continues.
J'utilise comme Target, pour la Regression Linéaire, la variable 'TotalGHGEmissions'

Le graphique ci-dessous affiche l'influence des points
en fonction des index des points de notre jeu de données.

On observe 1 point qui a particulièrement d'influence vers l'index 500,
ainsi que quelques autres qui se démarquent également mais dans une moindre mesure.

Le graphique nous informe que 4.68 % des points sont au dessus
de la limite qui définit les valeurs extrêmes qui seront supprimées.

Valeur du seuil :
Le seuil est calculé selon la formule 4/n où n représente le nombre d'échantillon

J'indique pour chaque index de mon jeu de données,
ceux qui sont définis avec une faible influence
:

Observons de nouveau la distribution des données de nos variables continues.
Les Outliers identifiés par Seaborn sont toujours présents.
Cependant les points les plus extrêmes ont été retirés,
précisément ceux qui étaient les plus influents et potentiellement nuisibles
à la qualité de prédiction des modèles de Machine Learning.

Encodage et Standardisation des données

**Les données catégorielles** :

Afin que nos données puissent être comprises par les algorithmes de Machines Learning,
les données catégorielles doivent être converties en données numériques.

Mes données qui n’ont pas de notion de distance mais ont cependant
une notion d’ordre seront encodés avec l’encodeur 'Label Encoder' ou 'Ordinal Encoder'.
'Ordinal Encoder' nous permet de gérer des données en plusieurs dimensions,
et nous permet surtout de définir l’ordre d’encodage de nos catégories.
'Label Encoder' est normalement prévu pour encoder les Labels, soit des données en 1 seule dimension.

J’utiliserai les deux méthodes dans le cadre de ce projet.

Mes données qui n’ont ni de notion d’ordre ni de notion de distance seront encodés avec un 'One-Hot-Encoder'.
A chaque catégorie sera associée une colonne qui lui sera propre, remplie uniquement de 0 ou de 1.

De plus, dans le cas où une variable aurait beaucoup de modalités
et où seulement certaines modalités regrouperaient à elles seules une part
importante des échantillons, alors je filtrerai le résultat de la transformation 'One-Hot-Encoder'
pour ne garder que ces modalités majoritaires.
Dans ce projet, et dans certains cas en fonction de la cardinalité des variables,
je choisirai de ne conserver que les modalités qui représentent au moins 80% des échantillons.

Par exemple, si une variable contient une liste de 100 fruits et légumes et que les fruits ‘pommes’
et ‘orange’ représentent à eux seul 80% des échantillons, alors je ne conserverai que ces deux modalités.

Pour mes variables contenant des List de valeurs, j’utiliserai l’encodeur 'MultiLabelEncodeur'.
Il reprend le principe du 'One-Hot-Encoder' mais ici plusieurs colonnes
peuvent-être à 1 là où une seule colonne peut-être à 1 avec l'encodeur 'One-Hot-Encoder'.

Les données continues :

Les données continues (avec une notion d’ordre et de distance),
doivent, dans la plus part des cas, être transformées
si nous voulons les optimiser avant de les donner à nos algorithmes de Machine Learning.
En effet, pour que des variables mesurées à différentes échelles,
aient des valeurs comparables, elles doivent préalablement être standardisées.

La standardisation consiste à transformer nos données de tel sorte
à ce que chaque variable ait une moyenne égale à 0 et un écart-type égale à 1.
Il faut pour cela soustraire chaque valeur à la moyenne initiale de notre variable
et diviser le total par l’écart type initial de notre variable.
Les données ainsi obtenues sont beaucoup plus simples à utiliser pour
la plupart des modèles statistiques
.

Je procèderai à différents types de transformation sur mes variables,
comme la Standardisation avec le transformeur 'StandardScaler'
ou le passage au Log avec le transformeur 'BoxCox'.

MAJ de ma liste de classification des variables catégorielles et continues

Certaines de ces features ont été supprimées lors de mon analyse.
Je mets à jour cette liste en ne conservant que les features encore existantes :

J'utilise un dictionnaire, pour plus de lisibilité à l'utilisation

Encodage des données catégorielles qui ont une notion d'ordre mais pas de distance

Affichage des variables à encoder avec la répartition de leurs modalités :

J'utilise l'encodeur OrdinalEncoder pour tranformer la feature 'Outlier'
car je souhaite classer manuellement l'ordre des catégories.

J'utiliserai l'encodeur LabelEncoder pour transformer la feature 'DefaultData'.

Encodage de 'Outlier' :

Je vérifie l'ordre de mes catégories :

Encodage de 'DefaultData' :

Visualisation des données encodées :

Encodage des données catégorielles
sans notion d'ordre ni distance

Liste des variables concernées :

Aperçu des variables concernées :

Encodage des features contenant des [listes] : 'YearsENERGYSTARCertified'
et 'ListOfAllPropertyUseTypes' avec MultiLabelBinarizer

Cet encodeur sert normalement à encoder un Label, soit une seule colonne.
J'opère donc séparément pour chaque colonne.

Analyse de la cardinalité de 'YearsENERGYSTARCertified'

J'affiche la liste des différentes valeurs présentes dans les listes
contenues dans la colonne '**YearsENERGYSTARCertified**'
:

Aperçu de la colonne '**YearsENERGYSTARCertified**' filtrée sans valeur Null :

Encodage de 'YearsENERGYSTARCertified'

Encodage et conservation des seules modalités qui représentent 80% des échantillons :

Aperçu du DataFrame '**df**' sans valeur null dans l'ensemble des colonnes transformées :

Analyse de la cardinalité de 'ListOfAllPropertyUseTypes'

J'affiche la liste des différentes valeurs présentes dans les listes
contenues dans la colonne '**ListOfAllPropertyUseTypes**'
:

Encodage de 'ListOfAllPropertyUseTypes'

Encodage et conservation des seules modalités qui représentent 83% des échantillons :
Je choisis volontairement 83% pour conserver la modalité 'DataCenter' que je juge, à priori, intéressante.

Aperçu des colonnes encodées, filtrées sans valeurs Null :

Analyse de la cardinalité des variables restantes
sans notion d'ordre ni distance

Je mets à jour ma liste des variables sans notion de distance ni d'ordre
en retirant les deux variables 'YearsENERGYSTARCertified' et 'ListOfAllPropertyUseTypes'

Cardinalité des variables '**NoDistance_NoOrdre**' à traiter :

Je traiterai les variables par lot suivant leur cardinalité.

Je traiterai dans l'odre :

Fonction d'automatisation pour OneHotEncoder

J'écris une fonction qui me permet d'automatiser la transformation
en One-Hot-Encoder d'une ou plusieurs variables.

Cette fonction reçoit en entrée un DataFrame
avec une List de colonne à transformer.
La fonction retourne le DataFrame avec les colonnes transmises transformées
et avec les colonnes d'origine retirées.

Cette fonction permet également :

Encodage des variables à faible cardinalité < 20

Encodage en **OneHotEncoder** des variables :

Affichons également la fréquence relative
des modalités des variables à encoder
:

J'applique une transformation OneHotEncoder pour ces 4 variables.
Je conserve également l'ensemble de leurs modalités compte tenu de leur faible cardinalité.

Je conserve l'ensemble des modalités des variables suivantes car
leurs modalités ne représentent pas l'ensemble des modalités existantes réellement:

Je supprime la modalité la moins représentée pour
la variable suivante car l'ensemble des modalités possibles est représenté.
La dernière modalité pourra donc être déduite :

Encodage des colonnes 'Neighborhood',
'CouncilDistrictCode' et 'BuildingType'

J'utilise ma fonction sans réduire les features en renseignant
l'argument percentFeatures à 1
et en laissant delLastModIfNoDummyValue à False.

Encodage de la colonne 'ComplianceStatus'

J'utilise maintenant ma fonction en renseignant
l'argument percentFeatures à 1
mais et en paramétrant delLastModIfNoDummyValue à True.

Aperçu des variables encodées :

Je mets à jour ma list et dictionnaire de variables catégorielles et continues

Encodage des variables à haute cardinalité >1000

Ici seule la variable 'Address' a une aussi haute cardinalité.

Les adresses étant très spécifiques, allant jusqu'au numéro de rue,
celà donne à cette variable une cardinalité proche de celle qu’aurait une variable d’ID.

Il est cependant possible de diminuer sa cardinalité en tentant
d'extraire les seuls noms de rue de chaque adresse.

Ensuite nous analyserons de nouveau cette variable
et jugerons s'il est utile de l’encoder ou non.

Rappelons la cardinalité de la variable '**Adress**'
ainsi que les dimensions du DataFrame '**df**'
:

Sans traitement préalable, l'adresse la plus représentée
représente moins de 0.5% des adresse existantes.

Encodage de la colonne 'Address'

La fonction que j'ai écris ci-dessous tente d'extraire
les seuls noms de rue d'une adresse donnée.
L'idée est de supprimer, à l'exception du nom,
tous les élements de l'adresse comme :

La cardinalité de la colonne 'Address' est maintenant réduite à 284.

Après traitement, l'adresse le plus représentée représente plus de 7%
des adresses existantes dans notre jeu de données.

Malgré l'efficacité du traitement effectué sur cette variable,
je choisis de ne pas l'encoder et de l'effacer
.

En effet plusieurs mêmes noms de rue coexistent au sein de la même ville,
avec des CP différents, ce qui rend imprécise la solution appliquée ci-dessus.

L'ajout d'une contrainte avec la variable 'ZipCode' sur les différentiations
d'adresses réaugmenteraient davantage la cardinalité de la variable 'Addresse'.

Même s'il est possible d'encoder ce genre de variable avec
des transformeur de type HashingEncoder qui diminue le nombre
de colonnes générées par rapport à un One-Hot-Encoder,
je décide simplement de retirer la variable ‘Address’ de mon DataFrame 'df'.

Encodage des variables restantes

Je mets à jour ma liste des variables sans notion de distance ni d'ordre
en retirant les six variables
:

Il nous reste à encoder les variables suivantes :

Visualisons la fréquence relative
des modalités des variables à encoder
:

J'applique une transformation **OneHotEncoder** pour ces 5 variables
en appliquant la stratégie suivante
:

Visualisation du DataFrame df avec l'ensemble des variables,
qui n'ont ni une notion d'Ordre, ni une notion de Distance,
complètement encodées

Standardisation et/ou passage au Log
des données Continues

Avant d’appliquer une transformation à nos variables continues,
il est important de choisir la transformation la plus adéquate à chaque variable.
Ce choix se fera en fonction de la distribution de chaque variable.

Afin de mieux comprendre nos données, nous allons visualiser leur distribution.
Nous cherchons, idéalement, à obtenir une distribution de type Normale.

Nous allons donc successivement :

  1. Créer une copie de notre DataFrame 'df'
  2. Standardiser les données à partir de la copie de nos données
  3. Visualiser la distribution des données après transformation
  4. Créer une nouvelle copie de 'df'
  5. Passer au Log les données à partir de la nouvelle copie de nos données
  6. Visualiser une nouvelle fois les données après transformation
  7. Choisir, pour chaque variable continue, la transformation retenue
  8. Transformer les variables continues à partir de 'df' selon les transformations choisies

Rappel des données continues à analyser et à transformer :

Visualisation de la distribution
des données avant Standardisation

Les échelles de données sont très disparates.
Les distributions ne ressemblent pas à une distribution Normale
à l'exception des colonnes 'Latitude' et 'Longitude'.

Les colonnes renseignant sur une superficie sont unimodales non centrées
Les autres colonnes sont multimodales.

Standardisation sur les variables continues

Création d'une copie du DataFrame **df** pour tester les transformations
sans conséquence sur le DataFrame principal
:

Aperçu des variables Standardisées :

Visualisation de la distribution
des données après Standardisation

Seules les variables 'Latitude' et 'Longitude' ont une distribution qui ressemble à une distribution Normale.
Je n'effectuerai pas de transformation Box-Cox sur ces deux variables.

Transformation et Visualisation de la distribution
après transformation Box-Cox

La transformation Box-Cox ne peut s'appliquer que sur des Series contenant des valeurs strictement positives.

Pour chaque variable continue de mon DataFrame, je testerai préalablement à chaque application
de la transformation s'il existe des valeurs égales à 0.
(Seule la colonne 'Longitude' contient des valeurs négatives)

Si tel est le cas, la valeur sera remplacée par une valeur très proche de 0 : 0.00001

Choix de transformation retenue pour chaque variable
et application des transformations choisies

Après analyse des différentes distributions après transformation via **Standard Scaler**
et **Box-Cox** sur mes variables contenant des données continues,
je répartis les transformations finales de cette façon
: Je supprime également les variables que je juge non pertinentes au regard de leur distribution

  1. Transformation via Standard Scaler
    • 'ageBuilding'
    • 'ENERGYSTARScore'
    • 'Latitude'
    • 'Longitude'
    • 'Proportion_Electricity'
    • 'Proportion_NaturalGas'
    • 'Proportion_Steam'
  2. Transformation via Box-Cox
    • 'NumberofFloors'
    • 'PropertyGFATotal'
    • 'PropertyGFABuilding'
    • 'LargestPropertyUseTypeGFA'
    • 'TotalGHGEmissions'
    • 'SiteEnergyUse'
  3. Suppression des variables
    • 'PropertyGFAParking'
    • 'SecondLargestPropertyUseTypeGFA'
    • 'ThirdLargestPropertyUseTypeGFA'
    • 'NbListOfAllPropertyUseTypes'

Transformation **StandardScaler** des variables sélectionnées :

Transformation **BoxCox** des variables sélectionnées :

Suppression des variables sélectionnées :

Mise à jour des variables continues dans le dictionnaire **dictVarCatCont** :

Visualisation de la distribution des valeurs continues après transformation sur le DataFrame df

Etude des corrélations

Qu'appelle-t-on corrélation ?

En probabilité, la corrélation entre plusieurs variables est
une notion liée à leur dépendance, ou indépendance les unes aux autres.
Cette corélation est très souvent réduite à la corrélation
linéaire entre variables quantitatives. C'est ce que nous ferons ici.

Nous utiliserons la fonction corr() de Pandas
pour calculer les coefficients de Pearson.

Le coefficient de Pearson est un indice reflétant
une relation linéaire entre deux variables continues.
Le coefficient de corrélation varie entre -1 et +1,
0 reflétant une relation nulle entre les deux variables,
une valeur négative (corrélation négative) signifiant que
lorsqu'une des variables augmente, l'autre diminue ;
tandis qu'une valeur positive (corrélation positive) indique
que les deux variables varient ensemble dans le même sens.

Je vais dans cette partie analyser les rapports de corrélation
qui existe entre les différentes variables de notre DataFrame 'df'.

Je vais diviser mon étude en 2 parties :

Etude des corrélations entre
les Features et les Targets

Les modèles de Machine Learning fonctionnent mieux à prédire nos données
à partir de caractéristiques (features) corrélées à nos Targets.

Nous allons identifier et mettre en évidence les variables les plus corrélées,
les plus importantes et donc les plus aptes à aider correctement
les algorithmes de Machine Learning à effectuer de bonne prédiction
dans le problème qui nous est posé.

J'affiche ci-dessous la *heatmap de corrélation*,
avec les colonnes filtrées sur les Targets pour plus de lisibilité
:

Etude de la corrélation des variables entre-elles

A l'inverse de la corrélation variable/target, une corrélation trop forte entre deux variables du jeu de données
peut nuire aux performances de notre modèle de Machine Learning.

J'affiche ci-dessous la heatmap de corrélation entre les variables quantitatives :

Features Selection

Maintenant que nous avons mieux compris les relations qui existent
entre nos variables et nos Targets, ainsi que nos variables entre-elles
nous allons voir comment faire de la sélection de variables.

Cette technique consiste, parmi toutes les variables disponibles dans un Dataset,
à trouver puis à sélectionner celles qui seront les plus utiles au développement
d’un modèle de Machine Learning
.

En effet, lorsqu'on fournit trop de variables à un modèle de Machine Learning,
et notamment des variables qui ne sont pas utiles au modèle,
alors cela impactera ses performances de façon négative.

Plusieurs techniques existent pour faire de la sélection de variables.

Dans ce projet, j’effectuerai ma sélection de variables avec l’algorithme Lasso
en utilisant le module LassoCV de Sklearn et en expliquant en détail chaque étape.

J’effectuerai deux sélections de variables différentes, une pour chacune de nos deux Targets.

Sauvegarde du DataFrame 'df'

Je sauvegarde préalablement notre DataFrame '**df**' dans un objet Pickle :

Feature Selection pour la target 'TotalGHGEmissions'
avec LassoCV

Avant d'appliquer le modèle LassoCV, je vais préalablement séparer nos données en deux groupes.
Les features de notre jeu de données, d'un côté, seront stockées dans la variable X
et notre target 'TotalGHGEmissions' sera stockée dans la variable y.

Je retirerai également la variable 'ENERGYSTARScore' dans cette étape.

Je séparerai ensuite c'est deux variables en deux groupes :

Les données seront séparées de cette manière dans les variables :

Pour optimiser les performances de l'algorithme Lasso,
j'appliquerai la technique de Cross Validation.

Cette technique consiste à entraîner puis valider notre modèle
sur plusieurs découpes possibles du train-set.
Ainsi le score obtenu sera celui de la moyenne des différentes
découpes possibles du train-set.

Ainsi, en découpant le train-set en 5 parties, on pourra entrainer
notre modèle sur les 4 premières parties puis le valider sur la cinquième partie.
Ensuite l'algorithme refera la même chose pour toutes les configurations possibles.

LassoCV :

Création de nos variables X (features) et y (target) :

Création de nos jeux de train et de test,
avec un random_state défini pour la reproductivité des résultats
:

Nous allons maintenant itérer notre modèle avec
plusieurs valeurs possibles d'alpha.
Je définis une gamme assez large d'alphas allant
de 10e-4 à 10e1 découpés en 400 échantillons.

A chaque itération, nous allons enregistrer dans une List :

Graphique illustrant l'erreur quadratique moyenne en fonction de alpha :
Alpha semble atteindre un minimum entre 10e-3 et 10e-2

Affichons précisément la valeur de alpha qui minimise l'erreur MSE (Mean Squared Error) :

Observons maintenant, pour le meilleur alpha calculé,
les coefficients affectés à chacune de nos variables.

Un coefficient à 0 signifie que le modèle a pénalisé
la variable correspondante au point de l'éliminer.

Observons maintenant la pénalisation des variables en fonction de alpha :

**154 variables ont été retenues par notre modèle** et 29 ont donc été éliminées.

Liste des variables sélectionnées :

Liste des variables éliminées :

Je filtre 'df' avec les colonnes sélectionnées par LassoCV
et j'enregistre le résultat dans un nouveau DataFrame 'df_Total'.
Je rajoute également à ce DataFrame la colonne 'ENERGYSTARScore' ainsi
que la colonne target qui lui correspond.

Feature Selection pour la target 'SiteEnergyUse'
avec LassoCV

Je répète maintenant exactement le même protocole
de sélection de variable que précédemment
:

Création de nos variables X (features) et y (target) :

Création de nos jeux de train et de test,
avec un random_state défini pour la reproductivité des résultats
:

Nous allons maintenant itérer notre modèle avec
plusieurs valeurs possibles d'alpha.
Je définis une gamme assez large d'alphas allant
de 10e-4 à 10e1 découpés en 400 échantillons.

A chaque itération, nous allons enregistrer dans une List :

Graphique illustrant l'erreur quadratique moyenne en fonction de alpha :
Alpha semble atteindre un minimum entre 10e-3 et 10e-2

Affichons précisément la valeur de alpha qui minimise l'erreur MSE (Mean Square Error) :

Observons maintenant, pour le meilleur alpha calculé,
les coefficients affectés à chacune de nos variables.

Un coefficient à 0 signifie que le modèle a pénalisé
la variable correspondante au point de l'éliminer.

Observons maintenant la pénalisation des variables en fonction de alpha :

**133 variables ont été retenues par notre modèle** et 50 ont donc été éliminées.

Liste des variables sélectionnées :

Liste des variables éliminées :

Je filtre 'df' avec les colonnes selectionnées par LassoCV
et j'enregistre le résultat dans un nouveau DataFrame 'df_Energy'.
Je rajoute également à ce DataFrame la colonne 'ENERGYSTARScore' ainsi
que la colonne target qui lui correspond.

Machine Learning

Notre jeu de données a été nettoyé, encodé, standardisé et
est maintenant optimisé pour le Machine Learning.

Dans cette partie dans allons répondre aux 2 problématiques qui nous ont été posées :

  1. Tenter de prédire les émissions de CO2 et la consommation totale d’énergie des bâtiments
    • Et déterminer par la même occasion le meilleur modèle pour y parvenir
  2. Evaluer l’intérêt de la variable 'ENERGYSTARScore'

Dans les deux cas, nous allons tester plusieurs algorithmes pour tenter de prédire,
avec le minimum d’erreur, l’émission de CO2 et la consommation totale d’énergie d’un bâtiment.

Comme il n’est pas possible, à priori, de savoir quel algorithme
saura le mieux généraliser notre problématique, et donc lequel nous donnera
les meilleurs résultats, je testerai et évaluerai différents algorithmes,
du plus simple au plus complexe
.

Enfin, nous étudierons la pertinence d'utiliser la variable 'ENERGYSTARScore'.

Ainsi, je diviserai ce chapitre en 2 parties :

  1. Tests et évaluations des algorithmes sur les Target 'TotalGHGEmissions'
    puis 'SiteEnergyUse' sans utilisation de la variable 'ENERGYSTARScore'
    • J'enregistrerai également le temps d'exécution de chaque algorithme dans cette partie.
  1. Tests et évaluations des algorithmes sur les Target 'TotalGHGEmissions'
    puis 'SiteEnergyUse' avec comparatif des scores avec et
    sans utilisation de la variable 'ENERGYSTARScore'
    • Les tests devront être réalisés sur le même nombre d'entrées, en conséquence,
      ces tests seront réalisés avec les entrées où la variable 'ENERGYSTARScore' est Non Null.

Voici dans l’ordre les algorithmes que nous allons tester :

Optimisation des Algorithmes :

Je réaliserai une recherche des meilleurs hyperparamètres
avec l’utilisation des modules GridSearchCV ou RandomizedSearchCV.

GridSearchCV permet d’exécuter et d’évaluer par validation croisée,
un modèle en testant 1 à 1 toutes les combinaisons d’hyperparamètres qu’on lui fournit.
GridSearchCV nous retournera alors la meilleure combinaison d’hyperparamètre
possible à utiliser en fonction des données d’entrainement qu’on lui a fourni.

RandomizedSearchCV repose sur le même principe que GridSearchCV
à la différence qu’il ne va pas chercher à tester toutes les combinaisons possibles,
mais à effectuer une recherche limitée et aléatoire des hyperparamètres fournis.
Le nombre de tests à effectuer est défini par l’utilisateur et dépendra du temps de calcul requis.
Il est préférable d’utiliser ce module lorsque la recherche exhaustive
de tous les hyperparamètres ne peut pas être réalisée sans une puissance
de calcul ou un temps raisonnable.

Evaluation des algorithmes

J’utiliserai 4 métriques pour juger de la qualité des algorithmes
et pour comparer entre eux leurs performances :

Pour rappel :

Sauvegarde des DataFrame Intermédiaire
dans des objets Pickle

Restauration des DataFrame Intermédiaire via Pickle

Fonction d'affichage des métrics

J'affiche et j'exporte dans un dictionnaire les résultats
de mes modèles via ma fonction displayMetrics()
Ma fonction displayBestAlgo() permet d'afficher et de mettre
en évidence (selon la métrique choisie) le meilleur algorithme
de Machine Learning parmi les algorithmes testés qui lui ont
été fournis dans le dictionnaire dictAlgoScore.

Déclaration de mes deux fonctions de gestion
d'affichage des scores et des meilleurs algorithmes
:

Recherche des meilleurs algorithmes
de Machine Learning pour prédire
'TotalGHGEmissions' et 'SiteEnergyUse'

Label = TotalGHGEmissions

Initialisation du DataFrame

Je remplace le contenu de 'df' par le contenu de 'df_Total',
le DataFrame filtré et optimisé pour la target 'TotalGHGEmissions'.

Initialisation du dictionnaire destiné à enregistrer
les performances des différents algorithmes

Selection des Features et Label

Création du train set et du test set

Application de modèle Dummy pour établir une baseline

J'enregistre le score de réference dans le dictionnaire *dictAlgoScore*
et j'affiche les résultats des différentes métriques
:

Affichage des prédictions '**y_pred**' en bleu en fonction
de la valeur réelle de y '**y_true**' en rouge
:

J'enregistre également la MSE de ma baseline afin de pouvoir l'afficher
dans des graphiques où on affichera l'erreur des algorithmes en fonction de certains paramètres
:

KNN Linéaire

Version Linéaire de l'algorithme des K plus proches voisins.

Je test l'algortithme avec différentes valeurs de k,
allant de k = 1 à k = 49.
Je procède cette fois-ci à une cross-validation.
J'affiche ensuite le score du train-set et du validation-set en fonction de k.

On observe un maximum pour le score du validation-set entre k=15 et k=17.

Je réalise maintenant une recherche des meilleurs *hyperparamètres*
avec **GridSearchCV** en testant les autres *hyperparamètres* existants
:

J'enregistre le score de réference dans le dictionnaire *dictAlgoScore*
et j'affiche les résultats des différentes métriques
:

Affichage de y_true en fonction de y_pred :

Regression Ridge

La régression Ridge est un régression linéaire avec une contrainte quadratique sur les coefficients.
Elle est utile lorsque les variables sont très corrélées, ce qui fausse souvent la résolution numérique.
La Régression Ridge va appliquer des pénalités aux variables corrélées
et va appliquer aux variables corrélées le même poids à chacune d'entre-elles.
De cette façon, les variables corrélées n'auront pas plus d'influence
qu'une variable seule non corrélée avec les autres variables du jeu de données.

La pénalité appliquée aux variables dépend d'un paramètre alpha.
Je vais donc tester la Régression Ridge avec plusieurs valeurs possibles d'alpha.

Je réalise une première application 'à la main' où j'afficherai les pénalités
appliquées aux variables en fonction de alpha, puis j'effectuerai dans un second temps
une recherche du meilleur alpha ainsi qu'une CrossValidation avec GridSearchCV.

Optimisation des *hyperparamètres* avec **GridSearchCV** :

J'enregistre le score de réference dans le dictionnaire *dictAlgoScore*
et j'affiche les résultats des différentes métriques
:

Affichage de y_true en fonction de y_pred :

Regression Lasso

La régression Lasso est une régression linéaire avec une contrainte linéaire sur les coefficients.
Elle est utile lorsque les variables sont très corrélées, ce qui fausse souvent la résolution numérique.
La Régression Lasso va appliquer des pénalités aux variables corrélées
et ne conserver qu'une seule variable parmi les variables corrélées
et passer le coefficiant des autres variables corrélées à 0.

La pénalité appliquée aux variables dépend d'un paramètre alpha.
Je vais donc tester la Régression Lasso avec plusieurs valeurs possibles d'alpha.

Je réalise une première application 'à la main' où j'afficherai les pénalités
appliquées aux variables en fonction de alpha, puis j'effectuerai dans un second temps
une recherche du meilleur alpha ainsi qu'une CrossValidation avec GridSearchCV.

Optimisation des *hyperparamètres* avec **GridSearchCV** :

J'enregistre le score de réference dans le dictionnaire *dictAlgoScore*
et j'affiche les résultats des différentes métriques
:

Affichage de y_true en fonction de y_pred :

Support Vector Regression (SVR)

Les algorithmes Support Vector Machine sont généralement utilisés dans des problèmes de classification.
L'idée de cet algorithme est de trouver un hyperplan qui divise notre jeu de données en deux.
SVR est la version Regression du Support Vector Machine (SVM).

Cet algorithme fonctionne bien sur de petits dataset.
Son entrainement peut-être long et il est sensible aux Outliers.

Premier essai avec les paramètres par défaut :

Optimisation des *hyperparamètres* avec **RandomizedSearchCV** :
J'utilise RandomizedSearchCV car la recherche des meilleurs hyperparamètres est particulièrement longue avec SVR

J'enregistre le score de réference dans le dictionnaire *dictAlgoScore*
et j'affiche les résultats des différentes métriques
:

Affichage de y_true en fonction de y_pred :

Regression Ridge à noyau (Kernel Ridge)

La Regression Ridge à noyau est une regression ridge
à laquelle nous appliquons l'astuce du noyau.
L'astuce du noyau (Kernel Trick) est une méthode qui permet
d'utiliser un classifieur linéaire pour résoudre un problème non linéaire.
L'idée est de transformer l'espace de représentation des données d'entrée
en un espace de plus grande dimension.
La discrimination linéaire dans l'espace de grande dimension
(appelé aussi espace de redescription) est équivalente à une discrimination
non linéaire dans l'espace d'origine.

Premier essai avec les paramètres par défaut :

Optimisation des *hyperparamètres* avec **GridSearchCV** :

J'enregistre le score de réference dans le dictionnaire *dictAlgoScore*
et j'affiche les résultats des différentes métriques
:

Affichage de y_true en fonction de y_pred :

Arbre de Décision Aléatoire (Random Forest)

L'algorithme Random Forest fait partie des algorithmes qui utilisent
la méthode du bagging et fait donc partie des algorithmes ensemblistes.

Rappel sur le bagging : Bagging est une contraction de Bootstrap Aggregation.
Le bagging a pour objectif de réduire la variance de l’estimateur (les problèmes liés
au surapprentissage, et donc qu'une petite modification en entrée (jeu de données) entraîne
de grandes différences en sortie).

L'idée est de créer plusieurs entités d'un même modèle
et d'entrainer chacune de ces entités sur une portion aléatoire
de notre jeu de données.
Pour cela on utilise une technique d'échantillonnage appelé Bootstrapping.
Cette technique consiste à replacer après chaque tirage au sort,
les données qui ont été sélectionnées dans notre jeu de données (on parle
alors de tirage avec remise).
De cette manière, on obtient un groupe de modèles diversifiés (ils n'ont
pas tous été nourris avec les mêmes données mais ils partagent cependant
certaines connaissances en commun) ce qui nous permet d’obtenir des majorités
en faveur des bonnes réponses.
On regroupe ensuite les résultats de chaque modèle pour faire notre prédiction finale.
Random Forest est l'algorithme le plus connu utilisant cette technique.
Il utilise comme modèle de base, l'arbre de décision.
Le nom de Random Forest est logiquement choisi comme tel car nous générons
des arbres de décision aléatoirement.
En cela nous créons donc une forêt aléatoire.

Entrainement avec les paramètres par défaut et n_estimator = 1000 :

J'enregistre le score de réference dans le dictionnaire *dictAlgoScore*
et j'affiche les résultats des différentes métriques
:

Affichage de y_true en fonction de y_pred :

AdaBoost

AdaBoost est une méthode de Boosting et fait donc
également partie des algorithmes ensemblistes.

L'idée est d'entraîner l'un après l'autre plusieurs modèle relativement faibles
en demandant à chaque modèle d'essayer de corriger les erreurs effectuées par son prédecesseur.
On obtient ainsi un ensemble de modèles complèmentaires dans lequel
les faiblesses des uns sont compensées par les forces des autres.

Ici les modèles sont entraînés en série.
Chaque modèle est en situation d'underfitting mais en les construisant
les uns par dessus les autres, on est capable de réduire le biais général de tous ces modèles.

Adaboost est un algorithme de boosting qui s’appuie sur ce principe,
avec un paramètre de mise à jour adaptatif permettant de donner plus
d’importance aux valeurs difficiles à prédire, donc en boostant les régresseurs
qui réussissent quand d’autres ont échoué.

Entrainement avec les paramètres par défaut :

Optimisation des *hyperparamètres* avec **GridSearchCV** :

J'enregistre le score de réference dans le dictionnaire *dictAlgoScore*
et j'affiche les résultats des différentes métriques
:

Affichage de y_true en fonction de y_pred :

GradientBoosting

Comme son nom l'indique, GradientBoosting est un algorithme de boosting.
En celà il reprend le même principe de fonctionnement qu'AdaBoost.

GradiantBoosting diffère d'AdaBoost sur sa fonction de perte,
qui s'appuie sur la descente de gradient.

La perte représente la pénalité générée lorsque l'estimation par le modèle
n'est pas parfaitement égale à la cible.
Une fonction de perte quantifie cette pénalité sous la forme d'une valeur individuelle.

La descente de gradient est un algorithme qui permet de trouver le minimum d’une fonction.
Le calcul de la dérivée en un point d'une fonction nous permet de définir
la pente de cette fonction et donc de définir le sens dans lequel il faut
se déplacer sur cette fonction pour se rapprocher de son minimum (local).

Entrainement avec les paramètres par défaut :

Optimisation des *hyperparamètres* avec **GridSearchCV** :

J'enregistre le score de réference dans le dictionnaire *dictAlgoScore*
et j'affiche les résultats des différentes métriques
:

Affichage de y_true en fonction de y_pred :

XGBoost

XGBoost signifie Extreme Gradient Boosting.
Il est assez similaire à l'algorithme GradientBoosting.
Il s'agit d'une implémentation spécifique du modèle Gradient Boosting
qui utilise des approximations plus précises pour trouver
le meilleur modèle d'arbre de décision.
Les deux algorithmes suivent le principe de descente de gradient.

XGBoost est plus rapide à l'exécution que GradientBoosting.

Liste non exaustive des avantages de XGBoost sur GradientBoosting :

Entrainement avec les paramètres par défaut :

Optimisation des *hyperparamètres* avec **GridSearchCV** :

J'enregistre le score de réference dans le dictionnaire *dictAlgoScore*
et j'affiche les résultats des différentes métriques
:

Affichage de y_true en fonction de y_pred :

Affichage des résulats des différents algorithmes

Label = SiteEnergyUse

Initialisation du DataFrame

Initialisation du dictionnaire destiné à enregistrer
les performances des différents algorithmes

Sélection des Features et Label

Création du train set et du test set

Application de modèle Dummy pour établir une baseline

J'enregistre le score de réference dans le dictionnaire *dictAlgoScore*
et j'affiche les résultats des différentes métriques
:

Affichage des prédictions '**y_pred**' en bleu en fonction
de la valeur réelle de y '**y_true**' en rouge
:

J'enregistre également la MSE de ma baseline afin de pouvoir l'afficher
dans des graphiques où on affichera l'erreur des algorithmes en fonction de certains paramètres
:

KNN Linéaire

Version Linéaire de l'algorithme des K plus proches voisins.

Je test l'algortithme avec différentes valeurs de k,
allant de k = 1 à k = 49.
Je procède cette fois-ci à une cross-validation.
J'affiche ensuite le score du train-set et du validation-set en fonction de k.

On observe un maximum pour le score du validation-set entre k=15 et k=17.

Je réalise maintenant une recherche des meilleurs *hyperparamètres*
avec **GridSearchCV** en testant les autres *hyperparamètres* existants
:

J'enregistre le score de réference dans le dictionnaire *dictAlgoScore*
et j'affiche les résultats des différentes métriques
:

Affichage de y_true en fonction de y_pred :

Regression Ridge

La régression Ridge est un régression linéaire avec une contrainte quadratique sur les coefficients.
Elle est utile lorsque les variables sont très corrélées, ce qui fausse souvent la résolution numérique.
La Régression Ridge va appliquer des pénalités aux variables corrélées
et va appliquer aux variables corrélées le même poids à chacune d'entre-elles.
De cette façon, les variables corrélées n'auront pas plus d'influence
qu'une variable seule non corrélée avec les autres variables du jeu de données.

La pénalité appliquée aux variables dépend d'un paramètre alpha.
Je vais donc tester la Régression Ridge avec plusieurs valeurs possibles d'alpha.

Je réalise une première application 'à la main' où j'afficherai les pénalités
appliquées aux variables en fonction de alpha, puis j'effectuerai dans un second temps
une recherche du meilleur alpha ainsi qu'une CrossValidation avec GridSearchCV.

Optimisation des *hyperparamètres* avec **GridSearchCV** :

J'enregistre le score de réference dans le dictionnaire *dictAlgoScore*
et j'affiche les résultats des différentes métriques
:

Affichage de y_true en fonction de y_pred :

Regression Lasso

La régression Lasso est une régression linéaire avec une contrainte linéaire sur les coefficients.
Elle est utile lorsque les variables sont très corrélées, ce qui fausse souvent la résolution numérique.
La Régression Lasso va appliquer des pénalités aux variables corrélées
et ne conserver qu'une seule variable parmi les variables corrélées
et passer le coefficiant des autres variables corrélées à 0.

La pénalité appliquée aux variables dépend d'un paramètre alpha.
Je vais donc tester la Régression Lasso avec plusieurs valeurs possibles d'alpha.

Je réalise une première application 'à la main' où j'afficherai les pénalités
appliquées aux variables en fonction de alpha, puis j'effectuerai dans un second temps
une recherche du meilleur alpha ainsi qu'une CrossValidation avec GridSearchCV.

Optimisation des *hyperparamètres* avec **GridSearchCV** :

J'enregistre le score de réference dans le dictionnaire *dictAlgoScore*
et j'affiche les résultats des différentes métriques
:

Affichage de y_true en fonction de y_pred :

Support Vector Regression (SVR)

Les algorithmes Support Vector Machine sont généralement utilisés dans des problèmes de classification.
L'idée de cet algorithme est de trouver un hyperplan qui divise notre jeu de données en deux.
SVR est la version Regression du Support Vector Machine (SVM).

Cet algorithme fonctionne bien sur de petits dataset.
Son entrainement peut-être long et il est sensible aux Outliers.

Premier essai avec les paramètres par défaut :

Optimisation des *hyperparamètres* avec **RandomizedSearchCV** :
J'utilise RandomizedSearchCV car la recherche des meilleurs hyperparamètres est particulièrement longue avec SVR

J'enregistre le score de réference dans le dictionnaire *dictAlgoScore*
et j'affiche les résultats des différentes métriques
:

Affichage de y_true en fonction de y_pred :

Regression Ridge à noyau (Kernel Ridge)

La Regression Ridge à noyau est une regression ridge
à laquelle nous appliquons l'astuce du noyau.
L'astuce du noyau (Kernel Trick) est une méthode qui permet
d'utiliser un classifieur linéaire pour résoudre un problème non linéaire.
L'idée est de transformer l'espace de représentation des données d'entrée
en un espace de plus grande dimension.
La discrimination linéaire dans l'espace de grande dimension
(appelé aussi espace de redescription) est équivalente à une discrimination
non linéaire dans l'espace d'origine.

Premier essai avec les paramètres par défaut :

Optimisation des *hyperparamètres* avec **GridSearchCV** :

J'enregistre le score de réference dans le dictionnaire *dictAlgoScore*
et j'affiche les résultats des différentes métriques
:

Affichage de y_true en fonction de y_pred :

Arbre de Décision Aléatoire (Random Forest)

L'algorithme Random Forest fait partie des algorithmes qui utilisent
la méthode du bagging et fait donc partie des algorithmes ensemblistes.

Rappel sur le bagging : Bagging est une contraction de Bootstrap Aggregation.
Le bagging a pour objectif de réduire la variance de l’estimateur (les problèmes liés
au surapprentissage, et donc qu'une petite modification en entrée (jeu de données) entraîne
de grandes différences en sortie).

L'idée est de créer plusieurs entités d'un même modèle
et d'entrainer chacune de ces entités sur une portion aléatoire
de notre jeu de données.
Pour cela on utilise une technique d'échantillonnage appelé Bootstrapping.
Cette technique consiste à replacer après chaque tirage au sort,
les données qui ont été sélectionnées dans notre jeu de données (on parle
alors de tirage avec remise).
De cette manière, on obtient un groupe de modèles diversifiés (ils n'ont
pas tous été nourris avec les mêmes données mais ils partagent cependant
certaines connaissances en commun) ce qui nous permet d’obtenir des majorités
en faveur des bonnes réponses.
On regroupe ensuite les résultats de chaque modèle pour faire notre prédiction finale.
Random Forest est l'algorithme le plus connu utilisant cette technique.
Il utilise comme modèle de base, l'arbre de décision.
Le nom de Random Forest est logiquement choisi comme tel car nous générons
des arbres de décision aléatoirement.
En cela nous créons donc une forêt aléatoire.

Entrainement avec les paramètres par défaut et n_estimator = 1000 :

J'enregistre le score de réference dans le dictionnaire *dictAlgoScore*
et j'affiche les résultats des différentes métriques
:

Affichage de y_true en fonction de y_pred :

AdaBoost

AdaBoost est une méthode de Boosting et fait donc
également partie des algorithmes ensemblistes.

L'idée est d'entraîner l'un après l'autre plusieurs modèle relativement faibles
en demandant à chaque modèle d'essayer de corriger les erreurs effectuées par son prédecesseur.
On obtient ainsi un ensemble de modèles complèmentaires dans lequel
les faiblesses des uns sont compensées par les forces des autres.

Ici les modèles sont entraînés en série.
Chaque modèle est en situation d'underfitting mais en les construisant
les uns par dessus les autres, on est capable de réduire le biais général de tous ces modèles.

Adaboost est un algorithme de boosting qui s’appuie sur ce principe,
avec un paramètre de mise à jour adaptatif permettant de donner plus
d’importance aux valeurs difficiles à prédire, donc en boostant les régresseurs
qui réussissent quand d’autres ont échoué.

Entrainement avec les paramètres par défaut :

Optimisation des *hyperparamètres* avec **GridSearchCV** :

J'enregistre le score de réference dans le dictionnaire *dictAlgoScore*
et j'affiche les résultats des différentes métriques
:

Affichage de y_true en fonction de y_pred :

GradientBoosting

Comme son nom l'indique, GradientBoosting est un algorithme de boosting.
En celà il reprend le même principe de fonctionnement qu'AdaBoost.

GradiantBoosting diffère d'AdaBoost sur sa fonction de perte,
qui s'appuie sur la descente de gradient.

La perte représente la pénalité générée lorsque l'estimation par le modèle
n'est pas parfaitement égale à la cible.
Une fonction de perte quantifie cette pénalité sous la forme d'une valeur individuelle.

La descente de gradient est un algorithme qui permet de trouver le minimum d’une fonction.
Le calcul de la dérivée en un point d'une fonction nous permet de définir
la pente de cette fonction et donc de définir le sens dans lequel il faut
se déplacer sur cette fonction pour se rapprocher de son minimum (local).

Entrainement avec les paramètres par défaut :

Optimisation des *hyperparamètres* avec **GridSearchCV** :

J'enregistre le score de réference dans le dictionnaire *dictAlgoScore*
et j'affiche les résultats des différentes métriques
:

Affichage de y_true en fonction de y_pred :

XGBoost

XGBoost signifie Extreme Gradient Boosting.
Il est assez similaire à l'algorithme GradientBoosting.
Il s'agit d'une implémentation spécifique du modèle Gradient Boosting
qui utilise des approximations plus précises pour trouver
le meilleur modèle d'arbre de décision.
Les deux algorithmes suivent le principe de descente de gradient.

XGBoost est plus rapide à l'exécution que GradientBoosting.

Liste non exaustive des avantages de XGBoost sur GradientBoosting :

Entrainement avec les paramètres par défaut :

Optimisation des *hyperparamètres* avec **GridSearchCV** :

J'enregistre le score de réference dans le dictionnaire *dictAlgoScore*
et j'affiche les résultats des différentes métriques
:

Affichage de y_true en fonction de y_pred :

Affichage des résulats des différents algorithmes

Interprétation des résultats

Nous devions réaliser 2 tests distincts sur les bâtiments
de la ville de Seattle (non destinés à l’habitation) afin
d'évaluer notre capacité à prédire :

Nous avons utilisé différents modèles de Machine Learning
pour évaluer notre capacité à prédire les émissions de CO2
et la consommation totale d’énergie.

D’une manière générale, si on se base sur la métrique **R²** :

D’une manière générale, si on se base sur la métrique **RMSE** :

Conclusion sur le modèle final sélectionné :

A la vue des différents résultats, sur les deux targets,
j’identifie XGBRegressor comme étant le meilleur algorithme
étant à même de faire de bonnes prédictions à la fois pour
les émissions de gaz à effet de serre et pour la consommation totale
en énergie
des bâtiments de Seattle.

Les algorithmes GradientBoostingRegressor et XGBRegressor ont
globalement des scores assez proches si on compare leur et leur RMSE.

Même si dans le cas présent, où nos essais ont été réalisés
avec un random_state fixé à 1, GradientBoostingRegressor
s’en sort légèrement mieux que XGBRegressor, ce n’est pas systématiquement le cas.
Globalement, sans random_state particulier, et après de nombreux essais,
j’ai pu observer que **XGBRegressor** s’en sort plus souvent avec
des meilleurs scores que **GradientBoostingRegressor**
.

De plus, XGBRegressor à plus de potentiel d’amélioration si on décide
d’aller plus loin dans l’optimisation de ses hyperparamètres même si
cela peut demander beaucoup de temps et de puissance de calcul.

Conclusion sur notre capacité à prédire efficacement
les émissions de CO2 et la consommation totale d'énergie
des bâtiments de la ville de Seattle
:

La performance des meilleurs algorithmes a démontré leurs capacités
à prédire efficacement les émissions de CO2 et la consommation totale d’énergie
des bâtiments non destinés à l’habitation de la ville de Seattle.

Evaluation de l'apport de la variable 'ENERGYSTARScore

Pour pouvoir évaluer l'apport de la variable ENERGYSTARScore,
il me faut évaluer mes modèles avec et sans cette variable.
Cependant, il faut également que les tests soient systématiquement
réalisés avec le même jeu de données (le même nombre d'entrées).

Je réalise ici les mêmes tests que précedement mais en filtrant
et en ne conservant uniquement que les enregistrements pour
lesquels **ENERGYSTARScore** est *Non Null*.

Je réaliserai les tests pour chaqu'une des 2 targets,
et en testant à chaque fois avec et sans l'utilisation
de la variable ENERGYSTARScore.

Label = TotalGHGEmissions | ENERGYSTARScore = OFF

Initialisation du DataFrame

Je remplace le contenu de 'df' par le contenu de 'df_Total',
le DataFrame filtré et optimisé pour la target 'TotalGHGEmissions'.

Initialisation du dictionnaire destiné à enregistrer
les performances des différents algorithmes

Sélection des Features et Label

Création du train set et du test set

Application de modèle Dummy pour établir une baseline

J'enregistre le score de réference dans le dictionnaire *dictAlgoScore*
et j'affiche les résultats des différentes métriques
:

Affichage des prédictions '**y_pred**' en bleu en fonction
de la valeur réelle de y '**y_true**' en rouge
:

J'enregistre également la MSE de ma baseline afin de pouvoir l'afficher
dans des graphiques où on affichera l'erreur des algorithmes en fonction de certains paramètres
:

KNN Linéaire

Version Linéaire de l'algorithme des K plus proches voisins.

Je test l'algortithme avec différentes valeurs de k,
allant de k = 1 à k = 49.
Je procède cette fois-ci à une cross-validation.
J'affiche ensuite le score du train-set et du validation-set en fonction de k.

On observe un maximum pour le score du validation-set entre k=15 et k=17.

Je réalise maintenant une recherche des meilleurs *hyperparamètres*
avec **GridSearchCV** en testant les autres *hyperparamètres* existants
:

J'enregistre le score de réference dans le dictionnaire *dictAlgoScore*
et j'affiche les résultats des différentes métriques
:

Affichage de y_true en fonction de y_pred :

Regression Ridge

La régression Ridge est un régression linéaire avec une contrainte quadratique sur les coefficients.
Elle est utile lorsque les variables sont très corrélées, ce qui fausse souvent la résolution numérique.
La Régression Ridge va appliquer des pénalités aux variables corrélées
et va appliquer aux variables corrélées le même poids à chacune d'entre-elles.
De cette façon, les variables corrélées n'auront pas plus d'influence
qu'une variable seule non corrélée avec les autres variables du jeu de données.

La pénalité appliquée aux variables dépend d'un paramètre alpha.
Je vais donc tester la Régression Ridge avec plusieurs valeurs possibles d'alpha.

Je réalise une première application 'à la main' où j'afficherai les pénalités
appliquées aux variables en fonction de alpha, puis j'effectuerai dans un second temps
une recherche du meilleur alpha ainsi qu'une CrossValidation avec GridSearchCV.

Optimisation des *hyperparamètres* avec **GridSearchCV** :

J'enregistre le score de réference dans le dictionnaire *dictAlgoScore*
et j'affiche les résultats des différentes métriques
:

Affichage de y_true en fonction de y_pred :

Regression Lasso

La régression Lasso est une régression linéaire avec une contrainte linéaire sur les coefficients.
Elle est utile lorsque les variables sont très corrélées, ce qui fausse souvent la résolution numérique.
La Régression Lasso va appliquer des pénalités aux variables corrélées
et ne conserver qu'une seule variable parmi les variables corrélées
et passer le coefficiant des autres variables corrélées à 0.

La pénalité appliquée aux variables dépend d'un paramètre alpha.
Je vais donc tester la Régression Lasso avec plusieurs valeurs possibles d'alpha.

Je réalise une première application 'à la main' où j'afficherai les pénalités
appliquées aux variables en fonction de alpha, puis j'effectuerai dans un second temps
une recherche du meilleur alpha ainsi qu'une CrossValidation avec GridSearchCV.

Optimisation des *hyperparamètres* avec **GridSearchCV** :

J'enregistre le score de réference dans le dictionnaire *dictAlgoScore*
et j'affiche les résultats des différentes métriques
:

Affichage de y_true en fonction de y_pred :

Support Vector Regression (SVR)

Les algorithmes Support Vector Machine sont généralement utilisés dans des problèmes de classification.
L'idée de cet algorithme est de trouver un hyperplan qui divise notre jeu de données en deux.
SVR est la version Regression du Support Vector Machine (SVM).

Cet algorithme fonctionne bien sur de petits dataset.
Son entrainement peut-être long et il est sensible aux Outliers.

Premier essai avec les paramètres par défaut :

Optimisation des *hyperparamètres* avec **RandomizedSearchCV** :
J'utilise RandomizedSearchCV car la recherche des meilleurs hyperparamètres est particulièrement longue avec SVR

J'enregistre le score de réference dans le dictionnaire *dictAlgoScore*
et j'affiche les résultats des différentes métriques
:

Affichage de y_true en fonction de y_pred :

Regression Ridge à noyau (Kernel Ridge)

La Regression Ridge à noyau est une regression ridge
à laquelle nous appliquons l'astuce du noyau.
L'astuce du noyau (Kernel Trick) est une méthode qui permet
d'utiliser un classifieur linéaire pour résoudre un problème non linéaire.
L'idée est de transformer l'espace de représentation des données d'entrée
en un espace de plus grande dimension.
La discrimination linéaire dans l'espace de grande dimension
(appelé aussi espace de redescription) est équivalente à une discrimination
non linéaire dans l'espace d'origine.

Premier essai avec les paramètres par défaut :

Optimisation des *hyperparamètres* avec **GridSearchCV** :

J'enregistre le score de réference dans le dictionnaire *dictAlgoScore*
et j'affiche les résultats des différentes métriques
:

Affichage de y_true en fonction de y_pred :

Arbre de Décision Aléatoire (Random Forest)

L'algorithme Random Forest fait partie des algorithmes qui utilisent
la méthode du bagging et fait donc partie des algorithmes ensemblistes.

Rappel sur le bagging : Bagging est une contraction de Bootstrap Aggregation.
Le bagging a pour objectif de réduire la variance de l’estimateur (les problèmes liés
au surapprentissage, et donc qu'une petite modification en entrée (jeu de données) entraîne
de grandes différences en sortie).

L'idée est de créer plusieurs entités d'un même modèle
et d'entrainer chacune de ces entités sur une portion aléatoire
de notre jeu de données.
Pour cela on utilise une technique d'échantillonnage appelé Bootstrapping.
Cette technique consiste à replacer après chaque tirage au sort,
les données qui ont été sélectionnées dans notre jeu de données (on parle
alors de tirage avec remise).
De cette manière, on obtient un groupe de modèles diversifiés (ils n'ont
pas tous été nourris avec les mêmes données mais ils partagent cependant
certaines connaissances en commun) ce qui nous permet d’obtenir des majorités
en faveur des bonnes réponses.
On regroupe ensuite les résultats de chaque modèle pour faire notre prédiction finale.
Random Forest est l'algorithme le plus connu utilisant cette technique.
Il utilise comme modèle de base, l'arbre de décision.
Le nom de Random Forest est logiquement choisi comme tel car nous générons
des arbres de décision aléatoirement.
En cela nous créons donc une forêt aléatoire.

Entrainement avec les paramètres par défaut et n_estimator = 1000 :

J'enregistre le score de réference dans le dictionnaire *dictAlgoScore*
et j'affiche les résultats des différentes métriques
:

Affichage de y_true en fonction de y_pred :

AdaBoost

AdaBoost est une méthode de Boosting et fait donc
également partie des algorithmes ensemblistes.

L'idée est d'entraîner l'un après l'autre plusieurs modèle relativement faibles
en demandant à chaque modèle d'essayer de corriger les erreurs effectuées par son prédecesseur.
On obtient ainsi un ensemble de modèles complèmentaires dans lequel
les faiblesses des uns sont compensées par les forces des autres.

Ici les modèles sont entraînés en série.
Chaque modèle est en situation d'underfitting mais en les construisant
les uns par dessus les autres, on est capable de réduire le biais général de tous ces modèles.

Adaboost est un algorithme de boosting qui s’appuie sur ce principe,
avec un paramètre de mise à jour adaptatif permettant de donner plus
d’importance aux valeurs difficiles à prédire, donc en boostant les régresseurs
qui réussissent quand d’autres ont échoué.

Entrainement avec les paramètres par défaut :

Optimisation des *hyperparamètres* avec **GridSearchCV** :

J'enregistre le score de réference dans le dictionnaire *dictAlgoScore*
et j'affiche les résultats des différentes métriques
:

Affichage de y_true en fonction de y_pred :

GradientBoosting

Comme son nom l'indique, GradientBoosting est un algorithme de boosting.
En celà il reprend le même principe de fonctionnement qu'AdaBoost.

GradiantBoosting diffère d'AdaBoost sur sa fonction de perte,
qui s'appuie sur la descente de gradient.

La perte représente la pénalité générée lorsque l'estimation par le modèle
n'est pas parfaitement égale à la cible.
Une fonction de perte quantifie cette pénalité sous la forme d'une valeur individuelle.

La descente de gradient est un algorithme qui permet de trouver le minimum d’une fonction.
Le calcul de la dérivée en un point d'une fonction nous permet de définir
la pente de cette fonction et donc de définir le sens dans lequel il faut
se déplacer sur cette fonction pour se rapprocher de son minimum (local).

Entrainement avec les paramètres par défaut :

Optimisation des *hyperparamètres* avec **GridSearchCV** :

J'enregistre le score de réference dans le dictionnaire *dictAlgoScore*
et j'affiche les résultats des différentes métriques
:

Affichage de y_true en fonction de y_pred :

XGBoost

XGBoost signifie Extreme Gradient Boosting.
Il est assez similaire à l'algorithme GradientBoosting.
Il s'agit d'une implémentation spécifique du modèle Gradient Boosting
qui utilise des approximations plus précises pour trouver
le meilleur modèle d'arbre de décision.
Les deux algorithmes suivent le principe de descente de gradient.

XGBoost est plus rapide à l'exécution que GradientBoosting.

Liste non exaustive des avantages de XGBoost sur GradientBoosting :

Entrainement avec les paramètres par défaut :

Optimisation des *hyperparamètres* avec **GridSearchCV** :

J'enregistre le score de réference dans le dictionnaire *dictAlgoScore*
et j'affiche les résultats des différentes métriques
:

Affichage de y_true en fonction de y_pred :

Affichage des résulats des différents algorithmes

Label = TotalGHGEmissions | ENERGYSTARScore = ON

Initialisation du DataFrame

Initialisation du dictionnaire destiné à enregistrer
les performances des différents algorithmes

Sélection des Features et Label

Création du train set et du test set

Application de modèle Dummy pour établir une baseline

J'enregistre le score de réference dans le dictionnaire *dictAlgoScore*
et j'affiche les résultats des différentes métriques
:

Affichage des prédictions '**y_pred**' en bleu en fonction
de la valeur réelle de y '**y_true**' en rouge
:

J'enregistre également la MSE de ma baseline afin de pouvoir l'afficher
dans des graphiques où on affichera l'erreur des algorithmes en fonction de certains paramètres
:

KNN Linéaire

Version Linéaire de l'algorithme des K plus proches voisins.

Je test l'algortithme avec différentes valeurs de k,
allant de k = 1 à k = 49.
Je procède cette fois-ci à une cross-validation.
J'affiche ensuite le score du train-set et du validation-set en fonction de k.

On observe un maximum pour le score du validation-set entre k=15 et k=17.

Je réalise maintenant une recherche des meilleurs *hyperparamètres*
avec **GridSearchCV** en testant les autres *hyperparamètres* existants
:

J'enregistre le score de réference dans le dictionnaire *dictAlgoScore*
et j'affiche les résultats des différentes métriques
:

Affichage de y_true en fonction de y_pred :

Regression Ridge

La régression Ridge est un régression linéaire avec une contrainte quadratique sur les coefficients.
Elle est utile lorsque les variables sont très corrélées, ce qui fausse souvent la résolution numérique.
La Régression Ridge va appliquer des pénalités aux variables corrélées
et va appliquer aux variables corrélées le même poids à chacune d'entre-elles.
De cette façon, les variables corrélées n'auront pas plus d'influence
qu'une variable seule non corrélée avec les autres variables du jeu de données.

La pénalité appliquée aux variables dépend d'un paramètre alpha.
Je vais donc tester la Régression Ridge avec plusieurs valeurs possibles d'alpha.

Je réalise une première application 'à la main' où j'afficherai les pénalités
appliquées aux variables en fonction de alpha, puis j'effectuerai dans un second temps
une recherche du meilleur alpha ainsi qu'une CrossValidation avec GridSearchCV.

Optimisation des *hyperparamètres* avec **GridSearchCV** :

J'enregistre le score de réference dans le dictionnaire *dictAlgoScore*
et j'affiche les résultats des différentes métriques
:

Affichage de y_true en fonction de y_pred :

Regression Lasso

La régression Lasso est une régression linéaire avec une contrainte linéaire sur les coefficients.
Elle est utile lorsque les variables sont très corrélées, ce qui fausse souvent la résolution numérique.
La Régression Lasso va appliquer des pénalités aux variables corrélées
et ne conserver qu'une seule variable parmi les variables corrélées
et passer le coefficiant des autres variables corrélées à 0.

La pénalité appliquée aux variables dépend d'un paramètre alpha.
Je vais donc tester la Régression Lasso avec plusieurs valeurs possibles d'alpha.

Je réalise une première application 'à la main' où j'afficherai les pénalités
appliquées aux variables en fonction de alpha, puis j'effectuerai dans un second temps
une recherche du meilleur alpha ainsi qu'une CrossValidation avec GridSearchCV.

Optimisation des *hyperparamètres* avec **GridSearchCV** :

J'enregistre le score de réference dans le dictionnaire *dictAlgoScore*
et j'affiche les résultats des différentes métriques
:

Affichage de y_true en fonction de y_pred :

Support Vector Regression (SVR)

Les algorithmes Support Vector Machine sont généralement utilisés dans des problèmes de classification.
L'idée de cet algorithme est de trouver un hyperplan qui divise notre jeu de données en deux.
SVR est la version Regression du Support Vector Machine (SVM).

Cet algorithme fonctionne bien sur de petits dataset.
Son entrainement peut-être long et il est sensible aux Outliers.

Premier essai avec les paramètres par défaut :

Optimisation des *hyperparamètres* avec **RandomizedSearchCV** :
J'utilise RandomizedSearchCV car la recherche des meilleurs hyperparamètres est particulièrement longue avec SVR

J'enregistre le score de réference dans le dictionnaire *dictAlgoScore*
et j'affiche les résultats des différentes métriques
:

Affichage de y_true en fonction de y_pred :

Regression Ridge à noyau (Kernel Ridge)

La Regression Ridge à noyau est une regression ridge
à laquelle nous appliquons l'astuce du noyau.
L'astuce du noyau (Kernel Trick) est une méthode qui permet
d'utiliser un classifieur linéaire pour résoudre un problème non linéaire.
L'idée est de transformer l'espace de représentation des données d'entrée
en un espace de plus grande dimension.
La discrimination linéaire dans l'espace de grande dimension
(appelé aussi espace de redescription) est équivalente à une discrimination
non linéaire dans l'espace d'origine.

Premier essai avec les paramètres par défaut :

Optimisation des *hyperparamètres* avec **GridSearchCV** :

J'enregistre le score de réference dans le dictionnaire *dictAlgoScore*
et j'affiche les résultats des différentes métriques
:

Affichage de y_true en fonction de y_pred :

Arbre de Décision Aléatoire (Random Forest)

L'algorithme Random Forest fait partie des algorithmes qui utilisent
la méthode du bagging et fait donc partie des algorithmes ensemblistes.

Rappel sur le bagging : Bagging est une contraction de Bootstrap Aggregation.
Le bagging a pour objectif de réduire la variance de l’estimateur (les problèmes liés
au surapprentissage, et donc qu'une petite modification en entrée (jeu de données) entraîne
de grandes différences en sortie).

L'idée est de créer plusieurs entités d'un même modèle
et d'entrainer chacune de ces entités sur une portion aléatoire
de notre jeu de données.
Pour cela on utilise une technique d'échantillonnage appelé Bootstrapping.
Cette technique consiste à replacer après chaque tirage au sort,
les données qui ont été sélectionnées dans notre jeu de données (on parle
alors de tirage avec remise).
De cette manière, on obtient un groupe de modèles diversifiés (ils n'ont
pas tous été nourris avec les mêmes données mais ils partagent cependant
certaines connaissances en commun) ce qui nous permet d’obtenir des majorités
en faveur des bonnes réponses.
On regroupe ensuite les résultats de chaque modèle pour faire notre prédiction finale.
Random Forest est l'algorithme le plus connu utilisant cette technique.
Il utilise comme modèle de base, l'arbre de décision.
Le nom de Random Forest est logiquement choisi comme tel car nous générons
des arbres de décision aléatoirement.
En cela nous créons donc une forêt aléatoire.

Entrainement avec les paramètres par défaut et n_estimator = 1000 :

J'enregistre le score de réference dans le dictionnaire *dictAlgoScore*
et j'affiche les résultats des différentes métriques
:

Affichage de y_true en fonction de y_pred :

AdaBoost

AdaBoost est une méthode de Boosting et fait donc
également partie des algorithmes ensemblistes.

L'idée est d'entraîner l'un après l'autre plusieurs modèle relativement faibles
en demandant à chaque modèle d'essayer de corriger les erreurs effectuées par son prédecesseur.
On obtient ainsi un ensemble de modèles complèmentaires dans lequel
les faiblesses des uns sont compensées par les forces des autres.

Ici les modèles sont entraînés en série.
Chaque modèle est en situation d'underfitting mais en les construisant
les uns par dessus les autres, on est capable de réduire le biais général de tous ces modèles.

Adaboost est un algorithme de boosting qui s’appuie sur ce principe,
avec un paramètre de mise à jour adaptatif permettant de donner plus
d’importance aux valeurs difficiles à prédire, donc en boostant les régresseurs
qui réussissent quand d’autres ont échoué.

Entrainement avec les paramètres par défaut :

Optimisation des *hyperparamètres* avec **GridSearchCV** :

J'enregistre le score de réference dans le dictionnaire *dictAlgoScore*
et j'affiche les résultats des différentes métriques
:

Affichage de y_true en fonction de y_pred :

GradientBoosting

Comme son nom l'indique, GradientBoosting est un algorithme de boosting.
En celà il reprend le même principe de fonctionnement qu'AdaBoost.

GradiantBoosting diffère d'AdaBoost sur sa fonction de perte,
qui s'appuie sur la descente de gradient.

La perte représente la pénalité générée lorsque l'estimation par le modèle
n'est pas parfaitement égale à la cible.
Une fonction de perte quantifie cette pénalité sous la forme d'une valeur individuelle.

La descente de gradient est un algorithme qui permet de trouver le minimum d’une fonction.
Le calcul de la dérivée en un point d'une fonction nous permet de définir
la pente de cette fonction et donc de définir le sens dans lequel il faut
se déplacer sur cette fonction pour se rapprocher de son minimum (local).

Entrainement avec les paramètres par défaut :

Optimisation des *hyperparamètres* avec **GridSearchCV** :

J'enregistre le score de réference dans le dictionnaire *dictAlgoScore*
et j'affiche les résultats des différentes métriques
:

Affichage de y_true en fonction de y_pred :

XGBoost

XGBoost signifie Extreme Gradient Boosting.
Il est assez similaire à l'algorithme GradientBoosting.
Il s'agit d'une implémentation spécifique du modèle Gradient Boosting
qui utilise des approximations plus précises pour trouver
le meilleur modèle d'arbre de décision.
Les deux algorithmes suivent le principe de descente de gradient.

XGBoost est plus rapide à l'exécution que GradientBoosting.

Liste non exaustive des avantages de XGBoost sur GradientBoosting :

Entrainement avec les paramètres par défaut :

Optimisation des *hyperparamètres* avec **GridSearchCV** :

J'enregistre le score de réference dans le dictionnaire *dictAlgoScore*
et j'affiche les résultats des différentes métriques
:

Affichage de y_true en fonction de y_pred :

Affichage des résulats des différents algorithmes

Label = SiteEnergyUse | ENERGYSTARScore = OFF

Initialisation du DataFrame

Initialisation du dictionnaire destiné à enregistrer
les performances des différents algorithmes

Sélection des Features et Label

Création du train set et du test set

Application de modèle Dummy pour établir une baseline

J'enregistre le score de réference dans le dictionnaire *dictAlgoScore*
et j'affiche les résultats des différentes métriques
:

Affichage des prédictions '**y_pred**' en bleu en fonction
de la valeur réelle de y '**y_true**' en rouge
:

J'enregistre également la MSE de ma baseline afin de pouvoir l'afficher
dans des graphiques où on affichera l'erreur des algorithmes en fonction de certains paramètres
:

KNN Linéaire

Version Linéaire de l'algorithme des K plus proches voisins.

Je test l'algortithme avec différentes valeurs de k,
allant de k = 1 à k = 49.
Je procède cette fois-ci à une cross-validation.
J'affiche ensuite le score du train-set et du validation-set en fonction de k.

On observe un maximum pour le score du validation-set entre k=15 et k=17.

Je réalise maintenant une recherche des meilleurs *hyperparamètres*
avec **GridSearchCV** en testant les autres *hyperparamètres* existants
:

J'enregistre le score de réference dans le dictionnaire *dictAlgoScore*
et j'affiche les résultats des différentes métriques
:

Affichage de y_true en fonction de y_pred :

Regression Ridge

La régression Ridge est un régression linéaire avec une contrainte quadratique sur les coefficients.
Elle est utile lorsque les variables sont très corrélées, ce qui fausse souvent la résolution numérique.
La Régression Ridge va appliquer des pénalités aux variables corrélées
et va appliquer aux variables corrélées le même poids à chacune d'entre-elles.
De cette façon, les variables corrélées n'auront pas plus d'influence
qu'une variable seule non corrélée avec les autres variables du jeu de données.

La pénalité appliquée aux variables dépend d'un paramètre alpha.
Je vais donc tester la Régression Ridge avec plusieurs valeurs possibles d'alpha.

Je réalise une première application 'à la main' où j'afficherai les pénalités
appliquées aux variables en fonction de alpha, puis j'effectuerai dans un second temps
une recherche du meilleur alpha ainsi qu'une CrossValidation avec GridSearchCV.

Optimisation des *hyperparamètres* avec **GridSearchCV** :

J'enregistre le score de réference dans le dictionnaire *dictAlgoScore*
et j'affiche les résultats des différentes métriques
:

Affichage de y_true en fonction de y_pred :

Regression Lasso

La régression Lasso est une régression linéaire avec une contrainte linéaire sur les coefficients.
Elle est utile lorsque les variables sont très corrélées, ce qui fausse souvent la résolution numérique.
La Régression Lasso va appliquer des pénalités aux variables corrélées
et ne conserver qu'une seule variable parmi les variables corrélées
et passer le coefficiant des autres variables corrélées à 0.

La pénalité appliquée aux variables dépend d'un paramètre alpha.
Je vais donc tester la Régression Lasso avec plusieurs valeurs possibles d'alpha.

Je réalise une première application 'à la main' où j'afficherai les pénalités
appliquées aux variables en fonction de alpha, puis j'effectuerai dans un second temps
une recherche du meilleur alpha ainsi qu'une CrossValidation avec GridSearchCV.

Optimisation des *hyperparamètres* avec **GridSearchCV** :

J'enregistre le score de réference dans le dictionnaire *dictAlgoScore*
et j'affiche les résultats des différentes métriques
:

Affichage de y_true en fonction de y_pred :

Support Vector Regression (SVR)

Les algorithmes Support Vector Machine sont généralement utilisés dans des problèmes de classification.
L'idée de cet algorithme est de trouver un hyperplan qui divise notre jeu de données en deux.
SVR est la version Regression du Support Vector Machine (SVM).

Cet algorithme fonctionne bien sur de petits dataset.
Son entrainement peut-être long et il est sensible aux Outliers.

Premier essai avec les paramètres par défaut :

Optimisation des *hyperparamètres* avec **RandomizedSearchCV** :
J'utilise RandomizedSearchCV car la recherche des meilleurs hyperparamètres est particulièrement longue avec SVR

J'enregistre le score de réference dans le dictionnaire *dictAlgoScore*
et j'affiche les résultats des différentes métriques
:

Affichage de y_true en fonction de y_pred :

Regression Ridge à noyau (Kernel Ridge)

La Regression Ridge à noyau est une regression ridge
à laquelle nous appliquons l'astuce du noyau.
L'astuce du noyau (Kernel Trick) est une méthode qui permet
d'utiliser un classifieur linéaire pour résoudre un problème non linéaire.
L'idée est de transformer l'espace de représentation des données d'entrée
en un espace de plus grande dimension.
La discrimination linéaire dans l'espace de grande dimension
(appelé aussi espace de redescription) est équivalente à une discrimination
non linéaire dans l'espace d'origine.

Premier essai avec les paramètres par défaut :

Optimisation des *hyperparamètres* avec **GridSearchCV** :

J'enregistre le score de réference dans le dictionnaire *dictAlgoScore*
et j'affiche les résultats des différentes métriques
:

Affichage de y_true en fonction de y_pred :

Arbre de Décision Aléatoire (Random Forest)

L'algorithme Random Forest fait partie des algorithmes qui utilisent
la méthode du bagging et fait donc partie des algorithmes ensemblistes.

Rappel sur le bagging : Bagging est une contraction de Bootstrap Aggregation.
Le bagging a pour objectif de réduire la variance de l’estimateur (les problèmes liés
au surapprentissage, et donc qu'une petite modification en entrée (jeu de données) entraîne
de grandes différences en sortie).

L'idée est de créer plusieurs entités d'un même modèle
et d'entrainer chacune de ces entités sur une portion aléatoire
de notre jeu de données.
Pour cela on utilise une technique d'échantillonnage appelé Bootstrapping.
Cette technique consiste à replacer après chaque tirage au sort,
les données qui ont été sélectionnées dans notre jeu de données (on parle
alors de tirage avec remise).
De cette manière, on obtient un groupe de modèles diversifiés (ils n'ont
pas tous été nourris avec les mêmes données mais ils partagent cependant
certaines connaissances en commun) ce qui nous permet d’obtenir des majorités
en faveur des bonnes réponses.
On regroupe ensuite les résultats de chaque modèle pour faire notre prédiction finale.
Random Forest est l'algorithme le plus connu utilisant cette technique.
Il utilise comme modèle de base, l'arbre de décision.
Le nom de Random Forest est logiquement choisi comme tel car nous générons
des arbres de décision aléatoirement.
En cela nous créons donc une forêt aléatoire.

Entrainement avec les paramètres par défaut et n_estimator = 1000 :

J'enregistre le score de réference dans le dictionnaire *dictAlgoScore*
et j'affiche les résultats des différentes métriques
:

Affichage de y_true en fonction de y_pred :

AdaBoost

AdaBoost est une méthode de Boosting et fait donc
également partie des algorithmes ensemblistes.

L'idée est d'entraîner l'un après l'autre plusieurs modèle relativement faibles
en demandant à chaque modèle d'essayer de corriger les erreurs effectuées par son prédecesseur.
On obtient ainsi un ensemble de modèles complèmentaires dans lequel
les faiblesses des uns sont compensées par les forces des autres.

Ici les modèles sont entraînés en série.
Chaque modèle est en situation d'underfitting mais en les construisant
les uns par dessus les autres, on est capable de réduire le biais général de tous ces modèles.

Adaboost est un algorithme de boosting qui s’appuie sur ce principe,
avec un paramètre de mise à jour adaptatif permettant de donner plus
d’importance aux valeurs difficiles à prédire, donc en boostant les régresseurs
qui réussissent quand d’autres ont échoué.

Entrainement avec les paramètres par défaut :

Optimisation des *hyperparamètres* avec **GridSearchCV** :

J'enregistre le score de réference dans le dictionnaire *dictAlgoScore*
et j'affiche les résultats des différentes métriques
:

Affichage de y_true en fonction de y_pred :

GradientBoosting

Comme son nom l'indique, GradientBoosting est un algorithme de boosting.
En celà il reprend le même principe de fonctionnement qu'AdaBoost.

GradiantBoosting diffère d'AdaBoost sur sa fonction de perte,
qui s'appuie sur la descente de gradient.

La perte représente la pénalité générée lorsque l'estimation par le modèle
n'est pas parfaitement égale à la cible.
Une fonction de perte quantifie cette pénalité sous la forme d'une valeur individuelle.

La descente de gradient est un algorithme qui permet de trouver le minimum d’une fonction.
Le calcul de la dérivée en un point d'une fonction nous permet de définir
la pente de cette fonction et donc de définir le sens dans lequel il faut
se déplacer sur cette fonction pour se rapprocher de son minimum (local).

Entrainement avec les paramètres par défaut :

Optimisation des *hyperparamètres* avec **GridSearchCV** :

J'enregistre le score de réference dans le dictionnaire *dictAlgoScore*
et j'affiche les résultats des différentes métriques
:

Affichage de y_true en fonction de y_pred :

XGBoost

XGBoost signifie Extreme Gradient Boosting.
Il est assez similaire à l'algorithme GradientBoosting.
Il s'agit d'une implémentation spécifique du modèle Gradient Boosting
qui utilise des approximations plus précises pour trouver
le meilleur modèle d'arbre de décision.
Les deux algorithmes suivent le principe de descente de gradient.

XGBoost est plus rapide à l'exécution que GradientBoosting.

Liste non exaustive des avantages de XGBoost sur GradientBoosting :

Entrainement avec les paramètres par défaut :

Optimisation des *hyperparamètres* avec **GridSearchCV** :

J'enregistre le score de réference dans le dictionnaire *dictAlgoScore*
et j'affiche les résultats des différentes métriques
:

Affichage de y_true en fonction de y_pred :

Affichage des résulats des différents algorithmes

Label = SiteEnergyUse | ENERGYSTARScore = ON

Initialisation du DataFrame

Initialisation du dictionnaire destiné à enregistrer
les performances des différents algorithmes

Sélection des Features et Label

Création du train set et du test set

Application de modèle Dummy pour établir une baseline

J'enregistre le score de réference dans le dictionnaire *dictAlgoScore*
et j'affiche les résultats des différentes métriques
:

Affichage des prédictions '**y_pred**' en bleu en fonction
de la valeur réelle de y '**y_true**' en rouge
:

J'enregistre également la MSE de ma baseline afin de pouvoir l'afficher
dans des graphiques où on affichera l'erreur des algorithmes en fonction de certains paramètres
:

KNN Linéaire

Version Linéaire de l'algorithme des K plus proches voisins.

Je test l'algortithme avec différentes valeurs de k,
allant de k = 1 à k = 49.
Je procède cette fois-ci à une cross-validation.
J'affiche ensuite le score du train-set et du validation-set en fonction de k.

On observe un maximum pour le score du validation-set entre k=15 et k=17.

Je réalise maintenant une recherche des meilleurs *hyperparamètres*
avec **GridSearchCV** en testant les autres *hyperparamètres* existants
:

J'enregistre le score de réference dans le dictionnaire *dictAlgoScore*
et j'affiche les résultats des différentes métriques
:

Affichage de y_true en fonction de y_pred :

Regression Ridge

La régression Ridge est un régression linéaire avec une contrainte quadratique sur les coefficients.
Elle est utile lorsque les variables sont très corrélées, ce qui fausse souvent la résolution numérique.
La Régression Ridge va appliquer des pénalités aux variables corrélées
et va appliquer aux variables corrélées le même poids à chacune d'entre-elles.
De cette façon, les variables corrélées n'auront pas plus d'influence
qu'une variable seule non corrélée avec les autres variables du jeu de données.

La pénalité appliquée aux variables dépend d'un paramètre alpha.
Je vais donc tester la Régression Ridge avec plusieurs valeurs possibles d'alpha.

Je réalise une première application 'à la main' où j'afficherai les pénalités
appliquées aux variables en fonction de alpha, puis j'effectuerai dans un second temps
une recherche du meilleur alpha ainsi qu'une CrossValidation avec GridSearchCV.

Optimisation des *hyperparamètres* avec **GridSearchCV** :

J'enregistre le score de réference dans le dictionnaire *dictAlgoScore*
et j'affiche les résultats des différentes métriques
:

Affichage de y_true en fonction de y_pred :

Regression Lasso

La régression Lasso est une régression linéaire avec une contrainte linéaire sur les coefficients.
Elle est utile lorsque les variables sont très corrélées, ce qui fausse souvent la résolution numérique.
La Régression Lasso va appliquer des pénalités aux variables corrélées
et ne conserver qu'une seule variable parmi les variables corrélées
et passer le coefficiant des autres variables corrélées à 0.

La pénalité appliquée aux variables dépend d'un paramètre alpha.
Je vais donc tester la Régression Lasso avec plusieurs valeurs possibles d'alpha.

Je réalise une première application 'à la main' où j'afficherai les pénalités
appliquées aux variables en fonction de alpha, puis j'effectuerai dans un second temps
une recherche du meilleur alpha ainsi qu'une CrossValidation avec GridSearchCV.

Optimisation des *hyperparamètres* avec **GridSearchCV** :

J'enregistre le score de réference dans le dictionnaire *dictAlgoScore*
et j'affiche les résultats des différentes métriques
:

Affichage de y_true en fonction de y_pred :

Support Vector Regression (SVR)

Les algorithmes Support Vector Machine sont généralement utilisés dans des problèmes de classification.
L'idée de cet algorithme est de trouver un hyperplan qui divise notre jeu de données en deux.
SVR est la version Regression du Support Vector Machine (SVM).

Cet algorithme fonctionne bien sur de petits dataset.
Son entrainement peut-être long et il est sensible aux Outliers.

Premier essai avec les paramètres par défaut :

Optimisation des *hyperparamètres* avec **RandomizedSearchCV** :
J'utilise RandomizedSearchCV car la recherche des meilleurs hyperparamètres est particulièrement longue avec SVR

J'enregistre le score de réference dans le dictionnaire *dictAlgoScore*
et j'affiche les résultats des différentes métriques
:

Affichage de y_true en fonction de y_pred :

Regression Ridge à noyau (Kernel Ridge)

La Regression Ridge à noyau est une regression ridge
à laquelle nous appliquons l'astuce du noyau.
L'astuce du noyau (Kernel Trick) est une méthode qui permet
d'utiliser un classifieur linéaire pour résoudre un problème non linéaire.
L'idée est de transformer l'espace de représentation des données d'entrée
en un espace de plus grande dimension.
La discrimination linéaire dans l'espace de grande dimension
(appelé aussi espace de redescription) est équivalente à une discrimination
non linéaire dans l'espace d'origine.

Premier essai avec les paramètres par défaut :

Optimisation des *hyperparamètres* avec **GridSearchCV** :

J'enregistre le score de réference dans le dictionnaire *dictAlgoScore*
et j'affiche les résultats des différentes métriques
:

Affichage de y_true en fonction de y_pred :

Arbre de Décision Aléatoire (Random Forest)

L'algorithme Random Forest fait partie des algorithmes qui utilisent
la méthode du bagging et fait donc partie des algorithmes ensemblistes.

Rappel sur le bagging : Bagging est une contraction de Bootstrap Aggregation.
Le bagging a pour objectif de réduire la variance de l’estimateur (les problèmes liés
au surapprentissage, et donc qu'une petite modification en entrée (jeu de données) entraîne
de grandes différences en sortie).

L'idée est de créer plusieurs entités d'un même modèle
et d'entrainer chacune de ces entités sur une portion aléatoire
de notre jeu de données.
Pour cela on utilise une technique d'échantillonnage appelé Bootstrapping.
Cette technique consiste à replacer après chaque tirage au sort,
les données qui ont été sélectionnées dans notre jeu de données (on parle
alors de tirage avec remise).
De cette manière, on obtient un groupe de modèles diversifiés (ils n'ont
pas tous été nourris avec les mêmes données mais ils partagent cependant
certaines connaissances en commun) ce qui nous permet d’obtenir des majorités
en faveur des bonnes réponses.
On regroupe ensuite les résultats de chaque modèle pour faire notre prédiction finale.
Random Forest est l'algorithme le plus connu utilisant cette technique.
Il utilise comme modèle de base, l'arbre de décision.
Le nom de Random Forest est logiquement choisi comme tel car nous générons
des arbres de décision aléatoirement.
En cela nous créons donc une forêt aléatoire.

Entrainement avec les paramètres par défaut et n_estimator = 1000 :

J'enregistre le score de réference dans le dictionnaire *dictAlgoScore*
et j'affiche les résultats des différentes métriques
:

Affichage de y_true en fonction de y_pred :

AdaBoost

AdaBoost est une méthode de Boosting et fait donc
également partie des algorithmes ensemblistes.

L'idée est d'entraîner l'un après l'autre plusieurs modèle relativement faibles
en demandant à chaque modèle d'essayer de corriger les erreurs effectuées par son prédecesseur.
On obtient ainsi un ensemble de modèles complèmentaires dans lequel
les faiblesses des uns sont compensées par les forces des autres.

Ici les modèles sont entraînés en série.
Chaque modèle est en situation d'underfitting mais en les construisant
les uns par dessus les autres, on est capable de réduire le biais général de tous ces modèles.

Adaboost est un algorithme de boosting qui s’appuie sur ce principe,
avec un paramètre de mise à jour adaptatif permettant de donner plus
d’importance aux valeurs difficiles à prédire, donc en boostant les régresseurs
qui réussissent quand d’autres ont échoué.

Entrainement avec les paramètres par défaut :

Optimisation des *hyperparamètres* avec **GridSearchCV** :

J'enregistre le score de réference dans le dictionnaire *dictAlgoScore*
et j'affiche les résultats des différentes métriques
:

Affichage de y_true en fonction de y_pred :

GradientBoosting

Comme son nom l'indique, GradientBoosting est un algorithme de boosting.
En celà il reprend le même principe de fonctionnement qu'AdaBoost.

GradiantBoosting diffère d'AdaBoost sur sa fonction de perte,
qui s'appuie sur la descente de gradient.

La perte représente la pénalité générée lorsque l'estimation par le modèle
n'est pas parfaitement égale à la cible.
Une fonction de perte quantifie cette pénalité sous la forme d'une valeur individuelle.

La descente de gradient est un algorithme qui permet de trouver le minimum d’une fonction.
Le calcul de la dérivée en un point d'une fonction nous permet de définir
la pente de cette fonction et donc de définir le sens dans lequel il faut
se déplacer sur cette fonction pour se rapprocher de son minimum (local).

Entrainement avec les paramètres par défaut :

Optimisation des *hyperparamètres* avec **GridSearchCV** :

J'enregistre le score de réference dans le dictionnaire *dictAlgoScore*
et j'affiche les résultats des différentes métriques
:

Affichage de y_true en fonction de y_pred :

XGBoost

XGBoost signifie Extreme Gradient Boosting.
Il est assez similaire à l'algorithme GradientBoosting.
Il s'agit d'une implémentation spécifique du modèle Gradient Boosting
qui utilise des approximations plus précises pour trouver
le meilleur modèle d'arbre de décision.
Les deux algorithmes suivent le principe de descente de gradient.

XGBoost est plus rapide à l'exécution que GradientBoosting.

Liste non exaustive des avantages de XGBoost sur GradientBoosting :

Entrainement avec les paramètres par défaut :

Optimisation des *hyperparamètres* avec **GridSearchCV** :

J'enregistre le score de réference dans le dictionnaire *dictAlgoScore*
et j'affiche les résultats des différentes métriques
:

Affichage de y_true en fonction de y_pred :

Affichage des résulats des différents algorithmes

Conclusion sur l’utilisation
de la variable ENERGYSTARScore
:

Nous avons filtré cette fois-ci notre jeu de données en ne conservant
que les bâtiments qui ont un ENERGYSTARScore renseigné.

Nous observons que le fait d’avoir filtré les données et dans le même temps
retiré certaines valeurs extrêmes de notre jeu de données a tout logiquement
optimisé nos scores, même sans utilisation de la variable ENERGYSTARScore.

Cependant, les scores nous indiquent que l'ajout de la variable ENERGYSTARScore
améliore la prédiction de tous nos modèles, à la fois pour prédire la target
TotalGHGEmissions mais également SiteEnergyUse.

Voici un résumé des scores des 2 meilleurs algorithmes
**sans** et **avec** utilisation de l'ENERGYSTARScore
:

Ces résultats confirment l'utilité de la variable ENERGYSTARScore.
Cependant, son apport, bien que non négligeable dans la qualité de prédiction
de nos modèles, ne démontre pas une nécessité d'utilisation si on prend en compte
la difficulté qu'ont les agents pour obtenir cette information avec les méthodes actuelles.

De plus, selon le temps, et l'énergie consacrée à obtenir cette donnée,
il peut être plus rentable de s'en passer compte tenu des bonnes performances de nos modèles.

Fin du projet